Reformat to Chromium style.

This commit is contained in:
Jacob Dufault 2017-09-21 18:14:57 -07:00
parent 1b1be28be4
commit f5314b62b1
58 changed files with 3677 additions and 2975 deletions

View File

@ -1,8 +1,9 @@
#include "cache.h" #include "cache.h"
#include "indexer.h" #include "indexer.h"
#include "platform.h"
#include "language_server_api.h" #include "language_server_api.h"
#include "platform.h"
#include <loguru/loguru.hpp> #include <loguru/loguru.hpp>
@ -43,15 +44,15 @@ optional<std::string> LoadCachedFileContents(Config* config,
return ReadContent(GetCachedBaseFileName(config->cacheDirectory, filename)); return ReadContent(GetCachedBaseFileName(config->cacheDirectory, filename));
} }
void WriteToCache(Config* config, void WriteToCache(Config* config, IndexFile& file) {
IndexFile& file) {
if (!config->enableCacheWrite) if (!config->enableCacheWrite)
return; return;
std::string cache_basename = std::string cache_basename =
GetCachedBaseFileName(config->cacheDirectory, file.path); GetCachedBaseFileName(config->cacheDirectory, file.path);
LOG_IF_S(ERROR, file.file_contents_.empty()) << "Writing " << file.path << " to cache but it has no contents"; LOG_IF_S(ERROR, file.file_contents_.empty())
<< "Writing " << file.path << " to cache but it has no contents";
assert(!file.file_contents_.empty()); assert(!file.file_contents_.empty());
std::ofstream cache_content; std::ofstream cache_content;

View File

@ -4,8 +4,8 @@
#include <memory> #include <memory>
#include <string> #include <string>
using std::experimental::optional;
using std::experimental::nullopt; using std::experimental::nullopt;
using std::experimental::optional;
struct Config; struct Config;
struct IndexFile; struct IndexFile;
@ -16,5 +16,4 @@ std::unique_ptr<IndexFile> LoadCachedIndex(Config* config,
optional<std::string> LoadCachedFileContents(Config* config, optional<std::string> LoadCachedFileContents(Config* config,
const std::string& filename); const std::string& filename);
void WriteToCache(Config* config, void WriteToCache(Config* config, IndexFile& file);
IndexFile& file);

View File

@ -15,7 +15,6 @@
#include <unistd.h> #include <unistd.h>
*/ */
namespace { namespace {
#if false #if false
@ -45,34 +44,37 @@ void EmitBacktrace() {
unsigned Flags() { unsigned Flags() {
// TODO: use clang_defaultEditingTranslationUnitOptions()? // TODO: use clang_defaultEditingTranslationUnitOptions()?
return return CXTranslationUnit_Incomplete | CXTranslationUnit_KeepGoing |
CXTranslationUnit_Incomplete | CXTranslationUnit_CacheCompletionResults |
CXTranslationUnit_KeepGoing | CXTranslationUnit_PrecompiledPreamble |
CXTranslationUnit_CacheCompletionResults | CXTranslationUnit_IncludeBriefCommentsInCodeCompletion
CXTranslationUnit_PrecompiledPreamble |
CXTranslationUnit_IncludeBriefCommentsInCodeCompletion
#if !defined(_WIN32) #if !defined(_WIN32)
// For whatever reason, CreatePreambleOnFirstParse causes clang to become // For whatever reason, CreatePreambleOnFirstParse causes clang to
// very crashy on windows. // become very crashy on windows.
// TODO: do more investigation, submit fixes to clang. // TODO: do more investigation, submit fixes to clang.
| CXTranslationUnit_CreatePreambleOnFirstParse | CXTranslationUnit_CreatePreambleOnFirstParse
#endif #endif
; ;
} }
int GetCompletionPriority(const CXCompletionString& str, CXCursorKind result_kind, const std::string& label) { int GetCompletionPriority(const CXCompletionString& str,
CXCursorKind result_kind,
const std::string& label) {
int priority = clang_getCompletionPriority(str); int priority = clang_getCompletionPriority(str);
if (result_kind == CXCursor_Destructor) { if (result_kind == CXCursor_Destructor) {
priority *= 100; priority *= 100;
//std::cerr << "Bumping[destructor] " << ls_completion_item.label << std::endl; // std::cerr << "Bumping[destructor] " << ls_completion_item.label <<
// std::endl;
} }
if (result_kind == CXCursor_ConversionFunction || if (result_kind == CXCursor_ConversionFunction ||
(result_kind == CXCursor_CXXMethod && StartsWith(label, "operator"))) { (result_kind == CXCursor_CXXMethod && StartsWith(label, "operator"))) {
//std::cerr << "Bumping[conversion] " << ls_completion_item.label << std::endl; // std::cerr << "Bumping[conversion] " << ls_completion_item.label <<
// std::endl;
priority *= 100; priority *= 100;
} }
if (clang_getCompletionAvailability(str) != CXAvailability_Available) { if (clang_getCompletionAvailability(str) != CXAvailability_Available) {
//std::cerr << "Bumping[notavailable] " << ls_completion_item.label << std::endl; // std::cerr << "Bumping[notavailable] " << ls_completion_item.label <<
// std::endl;
priority *= 100; priority *= 100;
} }
return priority; return priority;
@ -97,7 +99,6 @@ bool IsCallKind(CXCursorKind kind) {
lsCompletionItemKind GetCompletionKind(CXCursorKind cursor_kind) { lsCompletionItemKind GetCompletionKind(CXCursorKind cursor_kind) {
switch (cursor_kind) { switch (cursor_kind) {
case CXCursor_ObjCInstanceMethodDecl: case CXCursor_ObjCInstanceMethodDecl:
case CXCursor_CXXMethod: case CXCursor_CXXMethod:
return lsCompletionItemKind::Method; return lsCompletionItemKind::Method;
@ -146,128 +147,139 @@ lsCompletionItemKind GetCompletionKind(CXCursorKind cursor_kind) {
case CXCursor_TypeRef: case CXCursor_TypeRef:
return lsCompletionItemKind::Reference; return lsCompletionItemKind::Reference;
//return lsCompletionItemKind::Property; // return lsCompletionItemKind::Property;
//return lsCompletionItemKind::Unit; // return lsCompletionItemKind::Unit;
//return lsCompletionItemKind::Value; // return lsCompletionItemKind::Value;
//return lsCompletionItemKind::Keyword; // return lsCompletionItemKind::Keyword;
//return lsCompletionItemKind::Snippet; // return lsCompletionItemKind::Snippet;
//return lsCompletionItemKind::Color; // return lsCompletionItemKind::Color;
//return lsCompletionItemKind::File; // return lsCompletionItemKind::File;
case CXCursor_NotImplemented: case CXCursor_NotImplemented:
return lsCompletionItemKind::Text; return lsCompletionItemKind::Text;
default: default:
std::cerr << "[complete] Unhandled completion kind " << cursor_kind << std::endl; std::cerr << "[complete] Unhandled completion kind " << cursor_kind
<< std::endl;
return lsCompletionItemKind::Text; return lsCompletionItemKind::Text;
} }
} }
void BuildDetailString(CXCompletionString completion_string, std::string& label, std::string& detail, std::string& insert, std::vector<std::string>* parameters) { void BuildDetailString(CXCompletionString completion_string,
std::string& label,
std::string& detail,
std::string& insert,
std::vector<std::string>* parameters) {
int num_chunks = clang_getNumCompletionChunks(completion_string); int num_chunks = clang_getNumCompletionChunks(completion_string);
for (int i = 0; i < num_chunks; ++i) { for (int i = 0; i < num_chunks; ++i) {
CXCompletionChunkKind kind = clang_getCompletionChunkKind(completion_string, i); CXCompletionChunkKind kind =
clang_getCompletionChunkKind(completion_string, i);
switch (kind) { switch (kind) {
case CXCompletionChunk_Optional: { case CXCompletionChunk_Optional: {
CXCompletionString nested = clang_getCompletionChunkCompletionString(completion_string, i); CXCompletionString nested =
BuildDetailString(nested, label, detail, insert, parameters); clang_getCompletionChunkCompletionString(completion_string, i);
break; BuildDetailString(nested, label, detail, insert, parameters);
} break;
}
case CXCompletionChunk_Placeholder: { case CXCompletionChunk_Placeholder: {
std::string text = clang::ToString(clang_getCompletionChunkText(completion_string, i)); std::string text =
parameters->push_back(text); clang::ToString(clang_getCompletionChunkText(completion_string, i));
detail += text; parameters->push_back(text);
insert += "${" + std::to_string(parameters->size()) + ":" + text + "}"; detail += text;
break; insert += "${" + std::to_string(parameters->size()) + ":" + text + "}";
} break;
}
case CXCompletionChunk_CurrentParameter: case CXCompletionChunk_CurrentParameter:
// We have our own parsing logic for active parameter. This doesn't seem // We have our own parsing logic for active parameter. This doesn't seem
// to be very reliable. // to be very reliable.
break; break;
case CXCompletionChunk_TypedText: { case CXCompletionChunk_TypedText: {
std::string text = clang::ToString(clang_getCompletionChunkText(completion_string, i)); std::string text =
label = text; clang::ToString(clang_getCompletionChunkText(completion_string, i));
detail += text; label = text;
insert += text; detail += text;
break; insert += text;
} break;
}
case CXCompletionChunk_Text: { case CXCompletionChunk_Text: {
std::string text = clang::ToString(clang_getCompletionChunkText(completion_string, i)); std::string text =
detail += text; clang::ToString(clang_getCompletionChunkText(completion_string, i));
insert += text; detail += text;
break; insert += text;
} break;
}
case CXCompletionChunk_Informative: { case CXCompletionChunk_Informative: {
detail += clang::ToString(clang_getCompletionChunkText(completion_string, i)); detail +=
break; clang::ToString(clang_getCompletionChunkText(completion_string, i));
} break;
}
case CXCompletionChunk_ResultType: { case CXCompletionChunk_ResultType: {
CXString text = clang_getCompletionChunkText(completion_string, i); CXString text = clang_getCompletionChunkText(completion_string, i);
std::string new_detail = clang::ToString(text) + detail + " "; std::string new_detail = clang::ToString(text) + detail + " ";
detail = new_detail; detail = new_detail;
break; break;
} }
case CXCompletionChunk_LeftParen: case CXCompletionChunk_LeftParen:
detail += "("; detail += "(";
insert += "("; insert += "(";
break; break;
case CXCompletionChunk_RightParen: case CXCompletionChunk_RightParen:
detail += ")"; detail += ")";
insert += ")"; insert += ")";
break; break;
case CXCompletionChunk_LeftBracket: case CXCompletionChunk_LeftBracket:
detail += "["; detail += "[";
insert += "["; insert += "[";
break; break;
case CXCompletionChunk_RightBracket: case CXCompletionChunk_RightBracket:
detail += "]"; detail += "]";
insert += "]"; insert += "]";
break; break;
case CXCompletionChunk_LeftBrace: case CXCompletionChunk_LeftBrace:
detail += "{"; detail += "{";
insert += "{"; insert += "{";
break; break;
case CXCompletionChunk_RightBrace: case CXCompletionChunk_RightBrace:
detail += "}"; detail += "}";
insert += "}"; insert += "}";
break; break;
case CXCompletionChunk_LeftAngle: case CXCompletionChunk_LeftAngle:
detail += "<"; detail += "<";
insert += "<"; insert += "<";
break; break;
case CXCompletionChunk_RightAngle: case CXCompletionChunk_RightAngle:
detail += ">"; detail += ">";
insert += ">"; insert += ">";
break; break;
case CXCompletionChunk_Comma: case CXCompletionChunk_Comma:
detail += ", "; detail += ", ";
insert += ", "; insert += ", ";
break; break;
case CXCompletionChunk_Colon: case CXCompletionChunk_Colon:
detail += ":"; detail += ":";
insert += ":"; insert += ":";
break; break;
case CXCompletionChunk_SemiColon: case CXCompletionChunk_SemiColon:
detail += ";"; detail += ";";
insert += ";"; insert += ";";
break; break;
case CXCompletionChunk_Equal: case CXCompletionChunk_Equal:
detail += "="; detail += "=";
insert += "="; insert += "=";
break; break;
case CXCompletionChunk_HorizontalSpace: case CXCompletionChunk_HorizontalSpace:
case CXCompletionChunk_VerticalSpace: case CXCompletionChunk_VerticalSpace:
detail += " "; detail += " ";
insert += " "; insert += " ";
break; break;
} }
} }
} }
@ -285,9 +297,12 @@ void EnsureDocumentParsed(ClangCompleteManager* manager,
std::vector<CXUnsavedFile> unsaved = session->working_files->AsUnsavedFiles(); std::vector<CXUnsavedFile> unsaved = session->working_files->AsUnsavedFiles();
std::cerr << "[complete] Creating completion session with arguments " << StringJoin(args) << std::endl; std::cerr << "[complete] Creating completion session with arguments "
*tu = MakeUnique<clang::TranslationUnit>(index, session->file.filename, args, unsaved, Flags()); << StringJoin(args) << std::endl;
std::cerr << "[complete] Done creating active; did_fail=" << (*tu)->did_fail << std::endl; *tu = MakeUnique<clang::TranslationUnit>(index, session->file.filename, args,
unsaved, Flags());
std::cerr << "[complete] Done creating active; did_fail=" << (*tu)->did_fail
<< std::endl;
// Build diagnostics. // Build diagnostics.
if (manager->config_->diagnosticsOnParse && !(*tu)->did_fail) { if (manager->config_->diagnosticsOnParse && !(*tu)->did_fail) {
@ -306,11 +321,14 @@ void EnsureDocumentParsed(ClangCompleteManager* manager,
void CompletionParseMain(ClangCompleteManager* completion_manager) { void CompletionParseMain(ClangCompleteManager* completion_manager) {
while (true) { while (true) {
// Fetching the completion request blocks until we have a request. // Fetching the completion request blocks until we have a request.
ClangCompleteManager::ParseRequest request = completion_manager->parse_requests_.Dequeue(); ClangCompleteManager::ParseRequest request =
completion_manager->parse_requests_.Dequeue();
// If we don't get a session then that means we don't care about the file // If we don't get a session then that means we don't care about the file
// anymore - abandon the request. // anymore - abandon the request.
std::shared_ptr<CompletionSession> session = completion_manager->TryGetSession(request.path, false /*create_if_needed*/); std::shared_ptr<CompletionSession> session =
completion_manager->TryGetSession(request.path,
false /*create_if_needed*/);
if (!session) if (!session)
continue; continue;
@ -322,24 +340,29 @@ void CompletionParseMain(ClangCompleteManager* completion_manager) {
} }
std::unique_ptr<clang::TranslationUnit> parsing; std::unique_ptr<clang::TranslationUnit> parsing;
EnsureDocumentParsed(completion_manager, session, &parsing, &session->index); EnsureDocumentParsed(completion_manager, session, &parsing,
&session->index);
// Activate new translation unit. // Activate new translation unit.
// tu_last_parsed_at is only read by this thread, so it doesn't need to be under the mutex. // tu_last_parsed_at is only read by this thread, so it doesn't need to be
// under the mutex.
session->tu_last_parsed_at = std::chrono::high_resolution_clock::now(); session->tu_last_parsed_at = std::chrono::high_resolution_clock::now();
std::lock_guard<std::mutex> lock(session->tu_lock); std::lock_guard<std::mutex> lock(session->tu_lock);
std::cerr << "[completion] Swapping completion session for " << request.path << std::endl; std::cerr << "[completion] Swapping completion session for " << request.path
<< std::endl;
session->tu = std::move(parsing); session->tu = std::move(parsing);
} }
} }
void CompletionDiagnosticsDelayedRefreshMain(ClangCompleteManager* completion_manager) { void CompletionDiagnosticsDelayedRefreshMain(
ClangCompleteManager* completion_manager) {
constexpr int kSecondsToWaitForDiagnosticsRefresh = 5; constexpr int kSecondsToWaitForDiagnosticsRefresh = 5;
// Refreshes diagnostics a few seconds after the final code completion, since // Refreshes diagnostics a few seconds after the final code completion, since
// we don't get a language server request. // we don't get a language server request.
while (true) { while (true) {
std::unique_lock<std::mutex> l(completion_manager->delayed_diagnostic_wakeup_mtx_); std::unique_lock<std::mutex> l(
completion_manager->delayed_diagnostic_wakeup_mtx_);
completion_manager->delayed_diagnostic_wakeup_cv_.wait(l); completion_manager->delayed_diagnostic_wakeup_cv_.wait(l);
// Check for spurious wakeup. // Check for spurious wakeup.
@ -350,15 +373,19 @@ void CompletionDiagnosticsDelayedRefreshMain(ClangCompleteManager* completion_ma
// Get completion request info. // Get completion request info.
if (!l.owns_lock()) if (!l.owns_lock())
l.lock(); l.lock();
lsTextDocumentPositionParams location = *completion_manager->delayed_diagnostic_last_completion_position_; lsTextDocumentPositionParams location =
*completion_manager->delayed_diagnostic_last_completion_position_;
completion_manager->delayed_diagnostic_last_completion_position_.reset(); completion_manager->delayed_diagnostic_last_completion_position_.reset();
l.unlock(); l.unlock();
// Wait five seconds. If there was another completion request, start the // Wait five seconds. If there was another completion request, start the
// waiting process over again. // waiting process over again.
std::this_thread::sleep_for(std::chrono::seconds(kSecondsToWaitForDiagnosticsRefresh)); std::this_thread::sleep_for(
std::chrono::seconds(kSecondsToWaitForDiagnosticsRefresh));
l.lock(); l.lock();
bool has_completion_since_sleeping = completion_manager->delayed_diagnostic_last_completion_position_.has_value(); bool has_completion_since_sleeping =
completion_manager->delayed_diagnostic_last_completion_position_
.has_value();
l.unlock(); l.unlock();
if (has_completion_since_sleeping) if (has_completion_since_sleeping)
continue; continue;
@ -376,39 +403,42 @@ void CompletionDiagnosticsDelayedRefreshMain(ClangCompleteManager* completion_ma
completion_manager->completion_request_.SetIfEmpty(std::move(request)); completion_manager->completion_request_.SetIfEmpty(std::move(request));
break; break;
} }
} }
} }
void CompletionQueryMain(ClangCompleteManager* completion_manager) { void CompletionQueryMain(ClangCompleteManager* completion_manager) {
while (true) { while (true) {
// Fetching the completion request blocks until we have a request. // Fetching the completion request blocks until we have a request.
std::unique_ptr<ClangCompleteManager::CompletionRequest> request = completion_manager->completion_request_.Take(); std::unique_ptr<ClangCompleteManager::CompletionRequest> request =
completion_manager->completion_request_.Take();
std::string path = request->location.textDocument.uri.GetPath(); std::string path = request->location.textDocument.uri.GetPath();
std::shared_ptr<CompletionSession> session = completion_manager->TryGetSession(path, true /*create_if_needed*/); std::shared_ptr<CompletionSession> session =
completion_manager->TryGetSession(path, true /*create_if_needed*/);
std::lock_guard<std::mutex> lock(session->tu_lock); std::lock_guard<std::mutex> lock(session->tu_lock);
EnsureDocumentParsed(completion_manager, session, &session->tu, &session->index); EnsureDocumentParsed(completion_manager, session, &session->tu,
&session->index);
// Language server is 0-based, clang is 1-based. // Language server is 0-based, clang is 1-based.
unsigned line = request->location.position.line + 1; unsigned line = request->location.position.line + 1;
unsigned column = request->location.position.character + 1; unsigned column = request->location.position.character + 1;
std::cerr << "[complete] Completing at " << line << ":" << column << std::endl; std::cerr << "[complete] Completing at " << line << ":" << column
<< std::endl;
Timer timer; Timer timer;
std::vector<CXUnsavedFile> unsaved = completion_manager->working_files_->AsUnsavedFiles(); std::vector<CXUnsavedFile> unsaved =
completion_manager->working_files_->AsUnsavedFiles();
timer.ResetAndPrint("[complete] Fetching unsaved files"); timer.ResetAndPrint("[complete] Fetching unsaved files");
timer.Reset(); timer.Reset();
unsigned const kCompleteOptions = CXCodeComplete_IncludeMacros | CXCodeComplete_IncludeBriefComments; unsigned const kCompleteOptions =
CXCodeComplete_IncludeMacros | CXCodeComplete_IncludeBriefComments;
CXCodeCompleteResults* cx_results = clang_codeCompleteAt( CXCodeCompleteResults* cx_results = clang_codeCompleteAt(
session->tu->cx_tu, session->tu->cx_tu, session->file.filename.c_str(), line, column,
session->file.filename.c_str(), line, column, unsaved.data(), (unsigned)unsaved.size(), kCompleteOptions);
unsaved.data(), (unsigned)unsaved.size(),
kCompleteOptions);
if (!cx_results) { if (!cx_results) {
timer.ResetAndPrint("[complete] Code completion failed"); timer.ResetAndPrint("[complete] Code completion failed");
if (request->on_complete) if (request->on_complete)
@ -417,7 +447,8 @@ void CompletionQueryMain(ClangCompleteManager* completion_manager) {
} }
timer.ResetAndPrint("[complete] clangCodeCompleteAt"); timer.ResetAndPrint("[complete] clangCodeCompleteAt");
std::cerr << "[complete] Got " << cx_results->NumResults << " results" << std::endl; std::cerr << "[complete] Got " << cx_results->NumResults << " results"
<< std::endl;
{ {
if (request->on_complete) { if (request->on_complete) {
@ -428,14 +459,15 @@ void CompletionQueryMain(ClangCompleteManager* completion_manager) {
for (unsigned i = 0; i < cx_results->NumResults; ++i) { for (unsigned i = 0; i < cx_results->NumResults; ++i) {
CXCompletionResult& result = cx_results->Results[i]; CXCompletionResult& result = cx_results->Results[i];
// TODO: Try to figure out how we can hide base method calls without also // TODO: Try to figure out how we can hide base method calls without
// hiding method implementation assistance, ie, // also hiding method implementation assistance, ie,
// //
// void Foo::* { // void Foo::* {
// } // }
// //
if (clang_getCompletionAvailability(result.CompletionString) == CXAvailability_NotAvailable) if (clang_getCompletionAvailability(result.CompletionString) ==
CXAvailability_NotAvailable)
continue; continue;
// TODO: fill in more data // TODO: fill in more data
@ -443,29 +475,40 @@ void CompletionQueryMain(ClangCompleteManager* completion_manager) {
// kind/label/detail/docs/sortText // kind/label/detail/docs/sortText
ls_completion_item.kind = GetCompletionKind(result.CursorKind); ls_completion_item.kind = GetCompletionKind(result.CursorKind);
BuildDetailString(result.CompletionString, ls_completion_item.label, ls_completion_item.detail, ls_completion_item.insertText, &ls_completion_item.parameters_); BuildDetailString(result.CompletionString, ls_completion_item.label,
ls_completion_item.detail,
ls_completion_item.insertText,
&ls_completion_item.parameters_);
ls_completion_item.insertText += "$0"; ls_completion_item.insertText += "$0";
ls_completion_item.documentation = clang::ToString(clang_getCompletionBriefComment(result.CompletionString)); ls_completion_item.documentation = clang::ToString(
ls_completion_item.sortText = (const char)uint64_t(GetCompletionPriority(result.CompletionString, result.CursorKind, ls_completion_item.label)); clang_getCompletionBriefComment(result.CompletionString));
ls_completion_item.sortText = (const char)uint64_t(
GetCompletionPriority(result.CompletionString, result.CursorKind,
ls_completion_item.label));
ls_result.push_back(ls_completion_item); ls_result.push_back(ls_completion_item);
} }
timer.ResetAndPrint("[complete] Building " + std::to_string(ls_result.size()) + " completion results"); timer.ResetAndPrint("[complete] Building " +
std::to_string(ls_result.size()) +
" completion results");
request->on_complete(ls_result, false /*is_cached_result*/); request->on_complete(ls_result, false /*is_cached_result*/);
timer.ResetAndPrint("[complete] Running user-given completion func"); timer.ResetAndPrint("[complete] Running user-given completion func");
} }
if (completion_manager->config_->diagnosticsOnCodeCompletion) { if (completion_manager->config_->diagnosticsOnCodeCompletion) {
unsigned num_diagnostics = clang_codeCompleteGetNumDiagnostics(cx_results); unsigned num_diagnostics =
clang_codeCompleteGetNumDiagnostics(cx_results);
NonElidedVector<lsDiagnostic> ls_diagnostics; NonElidedVector<lsDiagnostic> ls_diagnostics;
for (unsigned i = 0; i < num_diagnostics; ++i) { for (unsigned i = 0; i < num_diagnostics; ++i) {
CXDiagnostic cx_diag = clang_codeCompleteGetDiagnostic(cx_results, i); CXDiagnostic cx_diag = clang_codeCompleteGetDiagnostic(cx_results, i);
optional<lsDiagnostic> diagnostic = BuildAndDisposeDiagnostic(cx_diag, path); optional<lsDiagnostic> diagnostic =
BuildAndDisposeDiagnostic(cx_diag, path);
if (diagnostic) if (diagnostic)
ls_diagnostics.push_back(*diagnostic); ls_diagnostics.push_back(*diagnostic);
} }
completion_manager->on_diagnostic_(session->file.filename, ls_diagnostics); completion_manager->on_diagnostic_(session->file.filename,
ls_diagnostics);
timer.ResetAndPrint("[complete] Build diagnostics"); timer.ResetAndPrint("[complete] Build diagnostics");
} }
} }
@ -477,8 +520,10 @@ void CompletionQueryMain(ClangCompleteManager* completion_manager) {
if (completion_manager->config_->diagnosticsOnCodeCompletion && if (completion_manager->config_->diagnosticsOnCodeCompletion &&
request->is_user_completion) { request->is_user_completion) {
{ {
std::lock_guard<std::mutex> lock(completion_manager->delayed_diagnostic_wakeup_mtx_); std::lock_guard<std::mutex> lock(
completion_manager->delayed_diagnostic_last_completion_position_ = request->location; completion_manager->delayed_diagnostic_wakeup_mtx_);
completion_manager->delayed_diagnostic_last_completion_position_ =
request->location;
} }
completion_manager->delayed_diagnostic_wakeup_cv_.notify_one(); completion_manager->delayed_diagnostic_wakeup_cv_.notify_one();
} }
@ -489,19 +534,25 @@ void CompletionQueryMain(ClangCompleteManager* completion_manager) {
} // namespace } // namespace
CompletionSession::CompletionSession(const Project::Entry& file, WorkingFiles* working_files) CompletionSession::CompletionSession(const Project::Entry& file,
: file(file), working_files(working_files), index(0 /*excludeDeclarationsFromPCH*/, 0 /*displayDiagnostics*/) { WorkingFiles* working_files)
std::cerr << "[complete] CompletionSession::CompletionSession() for " << file.filename << std::endl; : file(file),
working_files(working_files),
index(0 /*excludeDeclarationsFromPCH*/, 0 /*displayDiagnostics*/) {
std::cerr << "[complete] CompletionSession::CompletionSession() for "
<< file.filename << std::endl;
} }
CompletionSession::~CompletionSession() { CompletionSession::~CompletionSession() {
std::cerr << "[complete] CompletionSession::~CompletionSession() for " << file.filename << std::endl; std::cerr << "[complete] CompletionSession::~CompletionSession() for "
<< file.filename << std::endl;
// EmitBacktrace(); // EmitBacktrace();
} }
LruSessionCache::LruSessionCache(int max_entries) : max_entries_(max_entries) {} LruSessionCache::LruSessionCache(int max_entries) : max_entries_(max_entries) {}
std::shared_ptr<CompletionSession> LruSessionCache::TryGetEntry(const std::string& filename) { std::shared_ptr<CompletionSession> LruSessionCache::TryGetEntry(
const std::string& filename) {
for (int i = 0; i < entries_.size(); ++i) { for (int i = 0; i < entries_.size(); ++i) {
if (entries_[i]->file.filename == filename) if (entries_[i]->file.filename == filename)
return entries_[i]; return entries_[i];
@ -509,7 +560,8 @@ std::shared_ptr<CompletionSession> LruSessionCache::TryGetEntry(const std::strin
return nullptr; return nullptr;
} }
std::shared_ptr<CompletionSession> LruSessionCache::TryTakeEntry(const std::string& filename) { std::shared_ptr<CompletionSession> LruSessionCache::TryTakeEntry(
const std::string& filename) {
for (int i = 0; i < entries_.size(); ++i) { for (int i = 0; i < entries_.size(); ++i) {
if (entries_[i]->file.filename == filename) { if (entries_[i]->file.filename == filename) {
std::shared_ptr<CompletionSession> result = entries_[i]; std::shared_ptr<CompletionSession> result = entries_[i];
@ -527,11 +579,18 @@ void LruSessionCache::InsertEntry(std::shared_ptr<CompletionSession> session) {
} }
ClangCompleteManager::ParseRequest::ParseRequest(const std::string& path) ClangCompleteManager::ParseRequest::ParseRequest(const std::string& path)
: request_time(std::chrono::high_resolution_clock::now()), path(path) {} : request_time(std::chrono::high_resolution_clock::now()), path(path) {}
ClangCompleteManager::ClangCompleteManager(Config* config, Project* project, WorkingFiles* working_files, OnDiagnostic on_diagnostic) ClangCompleteManager::ClangCompleteManager(Config* config,
: config_(config), project_(project), working_files_(working_files), on_diagnostic_(on_diagnostic), Project* project,
view_sessions_(kMaxViewSessions), edit_sessions_(kMaxEditSessions) { WorkingFiles* working_files,
OnDiagnostic on_diagnostic)
: config_(config),
project_(project),
working_files_(working_files),
on_diagnostic_(on_diagnostic),
view_sessions_(kMaxViewSessions),
edit_sessions_(kMaxEditSessions) {
new std::thread([&]() { new std::thread([&]() {
SetCurrentThreadName("completequery"); SetCurrentThreadName("completequery");
CompletionQueryMain(this); CompletionQueryMain(this);
@ -550,7 +609,9 @@ ClangCompleteManager::ClangCompleteManager(Config* config, Project* project, Wor
ClangCompleteManager::~ClangCompleteManager() {} ClangCompleteManager::~ClangCompleteManager() {}
void ClangCompleteManager::CodeComplete(const lsTextDocumentPositionParams& completion_location, const OnComplete& on_complete) { void ClangCompleteManager::CodeComplete(
const lsTextDocumentPositionParams& completion_location,
const OnComplete& on_complete) {
// completion thread will create the CompletionSession if needed. // completion thread will create the CompletionSession if needed.
auto request = MakeUnique<CompletionRequest>(); auto request = MakeUnique<CompletionRequest>();
@ -572,9 +633,10 @@ void ClangCompleteManager::NotifyView(const std::string& filename) {
if (view_sessions_.TryGetEntry(filename)) if (view_sessions_.TryGetEntry(filename))
return; return;
std::cerr << "[complete] Creating new edit code completion session for " << filename << std::endl; std::cerr << "[complete] Creating new edit code completion session for "
<< filename << std::endl;
view_sessions_.InsertEntry(std::make_shared<CompletionSession>( view_sessions_.InsertEntry(std::make_shared<CompletionSession>(
project_->FindCompilationEntryForFile(filename), working_files_)); project_->FindCompilationEntryForFile(filename), working_files_));
parse_requests_.Enqueue(ParseRequest(filename)); parse_requests_.Enqueue(ParseRequest(filename));
} }
@ -590,14 +652,15 @@ void ClangCompleteManager::NotifyEdit(const std::string& filename) {
if (edit_sessions_.TryGetEntry(filename)) if (edit_sessions_.TryGetEntry(filename))
return; return;
std::shared_ptr<CompletionSession> session = view_sessions_.TryTakeEntry(filename); std::shared_ptr<CompletionSession> session =
view_sessions_.TryTakeEntry(filename);
if (session) { if (session) {
edit_sessions_.InsertEntry(session); edit_sessions_.InsertEntry(session);
} } else {
else { std::cerr << "[complete] Creating new edit code completion session for "
std::cerr << "[complete] Creating new edit code completion session for " << filename << std::endl; << filename << std::endl;
edit_sessions_.InsertEntry(std::make_shared<CompletionSession>( edit_sessions_.InsertEntry(std::make_shared<CompletionSession>(
project_->FindCompilationEntryForFile(filename), working_files_)); project_->FindCompilationEntryForFile(filename), working_files_));
parse_requests_.PriorityEnqueue(ParseRequest(filename)); parse_requests_.PriorityEnqueue(ParseRequest(filename));
} }
} }
@ -610,18 +673,22 @@ void ClangCompleteManager::NotifySave(const std::string& filename) {
std::lock_guard<std::mutex> lock(sessions_lock_); std::lock_guard<std::mutex> lock(sessions_lock_);
if (!edit_sessions_.TryGetEntry(filename)) { if (!edit_sessions_.TryGetEntry(filename)) {
std::cerr << "[complete] Creating new edit code completion session for " << filename << std::endl; std::cerr << "[complete] Creating new edit code completion session for "
<< filename << std::endl;
edit_sessions_.InsertEntry(std::make_shared<CompletionSession>( edit_sessions_.InsertEntry(std::make_shared<CompletionSession>(
project_->FindCompilationEntryForFile(filename), working_files_)); project_->FindCompilationEntryForFile(filename), working_files_));
} }
parse_requests_.PriorityEnqueue(ParseRequest(filename)); parse_requests_.PriorityEnqueue(ParseRequest(filename));
} }
std::shared_ptr<CompletionSession> ClangCompleteManager::TryGetSession(const std::string& filename, bool create_if_needed) { std::shared_ptr<CompletionSession> ClangCompleteManager::TryGetSession(
const std::string& filename,
bool create_if_needed) {
std::lock_guard<std::mutex> lock(sessions_lock_); std::lock_guard<std::mutex> lock(sessions_lock_);
std::shared_ptr<CompletionSession> session = edit_sessions_.TryGetEntry(filename); std::shared_ptr<CompletionSession> session =
edit_sessions_.TryGetEntry(filename);
if (!session) if (!session)
session = view_sessions_.TryGetEntry(filename); session = view_sessions_.TryGetEntry(filename);
@ -630,7 +697,7 @@ std::shared_ptr<CompletionSession> ClangCompleteManager::TryGetSession(const std
// Create new session. Default to edited_sessions_ since invoking code // Create new session. Default to edited_sessions_ since invoking code
// completion almost certainly implies an edit. // completion almost certainly implies an edit.
edit_sessions_.InsertEntry(std::make_shared<CompletionSession>( edit_sessions_.InsertEntry(std::make_shared<CompletionSession>(
project_->FindCompilationEntryForFile(filename), working_files_)); project_->FindCompilationEntryForFile(filename), working_files_));
session = edit_sessions_.TryGetEntry(filename); session = edit_sessions_.TryGetEntry(filename);
} }

View File

@ -13,13 +13,15 @@
#include <mutex> #include <mutex>
#include <string> #include <string>
struct CompletionSession : public std::enable_shared_from_this<CompletionSession> { struct CompletionSession
: public std::enable_shared_from_this<CompletionSession> {
Project::Entry file; Project::Entry file;
WorkingFiles* working_files; WorkingFiles* working_files;
clang::Index index; clang::Index index;
// When |tu| was last parsed. // When |tu| was last parsed.
optional<std::chrono::time_point<std::chrono::high_resolution_clock>> tu_last_parsed_at; optional<std::chrono::time_point<std::chrono::high_resolution_clock>>
tu_last_parsed_at;
// Acquired when |tu| is being used. // Acquired when |tu| is being used.
std::mutex tu_lock; std::mutex tu_lock;
@ -47,8 +49,12 @@ struct LruSessionCache {
}; };
struct ClangCompleteManager { struct ClangCompleteManager {
using OnDiagnostic = std::function<void(std::string path, NonElidedVector<lsDiagnostic> diagnostics)>; using OnDiagnostic =
using OnComplete = std::function<void(const NonElidedVector<lsCompletionItem>& results, bool is_cached_result)>; std::function<void(std::string path,
NonElidedVector<lsDiagnostic> diagnostics)>;
using OnComplete =
std::function<void(const NonElidedVector<lsCompletionItem>& results,
bool is_cached_result)>;
struct ParseRequest { struct ParseRequest {
ParseRequest(const std::string& path); ParseRequest(const std::string& path);
@ -62,7 +68,10 @@ struct ClangCompleteManager {
bool is_user_completion = false; bool is_user_completion = false;
}; };
ClangCompleteManager(Config* config, Project* project, WorkingFiles* working_files, OnDiagnostic on_diagnostic); ClangCompleteManager(Config* config,
Project* project,
WorkingFiles* working_files,
OnDiagnostic on_diagnostic);
~ClangCompleteManager(); ~ClangCompleteManager();
// Start a code completion at the given location. |on_complete| will run when // Start a code completion at the given location. |on_complete| will run when
@ -79,7 +88,8 @@ struct ClangCompleteManager {
// triggers a reparse. // triggers a reparse.
void NotifySave(const std::string& filename); void NotifySave(const std::string& filename);
std::shared_ptr<CompletionSession> TryGetSession(const std::string& filename, bool create_if_needed); std::shared_ptr<CompletionSession> TryGetSession(const std::string& filename,
bool create_if_needed);
// TODO: make these configurable. // TODO: make these configurable.
const int kMaxViewSessions = 1; const int kMaxViewSessions = 1;
@ -108,5 +118,6 @@ struct ClangCompleteManager {
std::mutex delayed_diagnostic_wakeup_mtx_; std::mutex delayed_diagnostic_wakeup_mtx_;
std::condition_variable delayed_diagnostic_wakeup_cv_; std::condition_variable delayed_diagnostic_wakeup_cv_;
// Access under |delayed_diagnostic_wakeup_mtx_|. // Access under |delayed_diagnostic_wakeup_mtx_|.
optional<lsTextDocumentPositionParams> delayed_diagnostic_last_completion_position_; optional<lsTextDocumentPositionParams>
delayed_diagnostic_last_completion_position_;
}; };

View File

@ -10,24 +10,24 @@ lsRange GetLsRangeForFixIt(const CXSourceRange& range) {
CXSourceLocation end = clang_getRangeEnd(range); CXSourceLocation end = clang_getRangeEnd(range);
unsigned int start_line, start_column; unsigned int start_line, start_column;
clang_getSpellingLocation(start, nullptr, &start_line, &start_column, nullptr); clang_getSpellingLocation(start, nullptr, &start_line, &start_column,
nullptr);
unsigned int end_line, end_column; unsigned int end_line, end_column;
clang_getSpellingLocation(end, nullptr, &end_line, &end_column, nullptr); clang_getSpellingLocation(end, nullptr, &end_line, &end_column, nullptr);
return lsRange( return lsRange(lsPosition(start_line - 1, start_column - 1) /*start*/,
lsPosition(start_line - 1, start_column - 1) /*start*/, lsPosition(end_line - 1, end_column) /*end*/);
lsPosition(end_line - 1, end_column) /*end*/);
} }
} // namespace } // namespace
optional<lsDiagnostic> BuildAndDisposeDiagnostic( optional<lsDiagnostic> BuildAndDisposeDiagnostic(CXDiagnostic diagnostic,
CXDiagnostic diagnostic, const std::string& path) { const std::string& path) {
// Get diagnostic location. // Get diagnostic location.
CXFile file; CXFile file;
unsigned int line, column; unsigned int line, column;
clang_getSpellingLocation( clang_getSpellingLocation(clang_getDiagnosticLocation(diagnostic), &file,
clang_getDiagnosticLocation(diagnostic), &file, &line, &column, nullptr); &line, &column, nullptr);
// Only report diagnostics in the same file. Using // Only report diagnostics in the same file. Using
// clang_Location_isInSystemHeader causes crashes for some reason. // clang_Location_isInSystemHeader causes crashes for some reason.
@ -43,12 +43,15 @@ optional<lsDiagnostic> BuildAndDisposeDiagnostic(
// TODO: ls_diagnostic.range is lsRange, we have Range. We should only be // TODO: ls_diagnostic.range is lsRange, we have Range. We should only be
// storing Range types when inside the indexer so that index <-> buffer // storing Range types when inside the indexer so that index <-> buffer
// remapping logic is applied. // remapping logic is applied.
ls_diagnostic.range = lsRange(lsPosition(line - 1, column), lsPosition(line - 1, column)); ls_diagnostic.range =
lsRange(lsPosition(line - 1, column), lsPosition(line - 1, column));
ls_diagnostic.message = clang::ToString(clang_getDiagnosticSpelling(diagnostic)); ls_diagnostic.message =
clang::ToString(clang_getDiagnosticSpelling(diagnostic));
// Append the flag that enables this diagnostic, ie, [-Wswitch] // Append the flag that enables this diagnostic, ie, [-Wswitch]
std::string enabling_flag = clang::ToString(clang_getDiagnosticOption(diagnostic, nullptr)); std::string enabling_flag =
clang::ToString(clang_getDiagnosticOption(diagnostic, nullptr));
if (!enabling_flag.empty()) if (!enabling_flag.empty())
ls_diagnostic.message += " [" + enabling_flag + "]"; ls_diagnostic.message += " [" + enabling_flag + "]";

View File

@ -4,13 +4,14 @@
#include <clang-c/Index.h> #include <clang-c/Index.h>
#include <vector>
#include <optional.h> #include <optional.h>
#include <vector>
using namespace std::experimental; using namespace std::experimental;
optional<lsDiagnostic> BuildAndDisposeDiagnostic( optional<lsDiagnostic> BuildAndDisposeDiagnostic(CXDiagnostic diagnostic,
CXDiagnostic diagnostic, const std::string& path); const std::string& path);
// Returns the absolute path to |file|. // Returns the absolute path to |file|.
std::string FileName(CXFile file); std::string FileName(CXFile file);

File diff suppressed because it is too large Load Diff

View File

@ -17,7 +17,7 @@ struct Config {
// If true, project paths that were skipped by the whitelist/blacklist will // If true, project paths that were skipped by the whitelist/blacklist will
// be logged. // be logged.
bool logSkippedPathsForIndex = false; bool logSkippedPathsForIndex = false;
// Maximum workspace search results. // Maximum workspace search results.
int maxWorkspaceSearchResults = 1000; int maxWorkspaceSearchResults = 1000;
@ -63,26 +63,30 @@ struct Config {
int clientVersion = 0; int clientVersion = 0;
}; };
MAKE_REFLECT_STRUCT(Config, MAKE_REFLECT_STRUCT(Config,
cacheDirectory, cacheDirectory,
extraClangArguments, extraClangArguments,
indexWhitelist, indexBlacklist, indexWhitelist,
logSkippedPathsForIndex, indexBlacklist,
logSkippedPathsForIndex,
maxWorkspaceSearchResults, maxWorkspaceSearchResults,
indexerCount, indexerCount,
enableIndexing, enableCacheWrite, enableCacheRead, enableIndexing,
enableCacheWrite,
enableCacheRead,
includeCompletionMaximumPathLength, includeCompletionMaximumPathLength,
includeCompletionWhitelistLiteralEnding, includeCompletionWhitelistLiteralEnding,
includeCompletionWhitelist, includeCompletionBlacklist, includeCompletionWhitelist,
includeCompletionBlacklist,
showDocumentLinksOnIncludes, showDocumentLinksOnIncludes,
diagnosticsOnParse, diagnosticsOnParse,
diagnosticsOnCodeCompletion, diagnosticsOnCodeCompletion,
codeLensOnLocalVariables, codeLensOnLocalVariables,
clientVersion); clientVersion);

View File

@ -6,7 +6,8 @@
#include "utils.h" #include "utils.h"
bool operator==(const CXFileUniqueID& a, const CXFileUniqueID& b) { bool operator==(const CXFileUniqueID& a, const CXFileUniqueID& b) {
return a.data[0] == b.data[0] && a.data[1] == b.data[1] && a.data[2] == b.data[2]; return a.data[0] == b.data[0] && a.data[1] == b.data[1] &&
a.data[2] == b.data[2];
} }
bool FileConsumer::SharedState::Mark(const std::string& file) { bool FileConsumer::SharedState::Mark(const std::string& file) {
@ -21,7 +22,8 @@ void FileConsumer::SharedState::Reset(const std::string& file) {
files.erase(it); files.erase(it);
} }
FileConsumer::FileConsumer(SharedState* shared_state, const std::string& parse_file) FileConsumer::FileConsumer(SharedState* shared_state,
const std::string& parse_file)
: shared_(shared_state), parse_file_(parse_file) {} : shared_(shared_state), parse_file_(parse_file) {}
IndexFile* FileConsumer::TryConsumeFile(CXFile file, bool* is_first_ownership) { IndexFile* FileConsumer::TryConsumeFile(CXFile file, bool* is_first_ownership) {
@ -86,7 +88,8 @@ void FileConsumer::EmitError(CXFile file) const {
std::string file_name = clang::ToString(clang_getFileName(file)); std::string file_name = clang::ToString(clang_getFileName(file));
// TODO: Investigate this more, why can we get an empty file name? // TODO: Investigate this more, why can we get an empty file name?
if (!file_name.empty()) { if (!file_name.empty()) {
std::string error_message = "Could not get unique file id for " + file_name + " when parsing " + parse_file_; std::string error_message = "Could not get unique file id for " +
file_name + " when parsing " + parse_file_;
std::cerr << error_message << std::endl; std::cerr << error_message << std::endl;
} }
} }

View File

@ -6,8 +6,9 @@
#include <functional> #include <functional>
#include <mutex> #include <mutex>
#include <unordered_set>
#include <unordered_map> #include <unordered_map>
#include <unordered_set>
struct IndexFile; struct IndexFile;

View File

@ -27,7 +27,8 @@ std::string ElideLongPath(Config* config, const std::string& path) {
return ".." + path.substr(start + 2); return ".." + path.substr(start + 2);
} }
size_t TrimCommonPathPrefix(const std::string& result, const std::string& trimmer) { size_t TrimCommonPathPrefix(const std::string& result,
const std::string& trimmer) {
size_t i = 0; size_t i = 0;
while (i < result.size() && i < trimmer.size()) { while (i < result.size() && i < trimmer.size()) {
char a = result[i]; char a = result[i];
@ -47,7 +48,9 @@ size_t TrimCommonPathPrefix(const std::string& result, const std::string& trimme
} }
// Returns true iff angle brackets should be used. // Returns true iff angle brackets should be used.
bool TrimPath(Project* project, const std::string& project_root, std::string* insert_path) { bool TrimPath(Project* project,
const std::string& project_root,
std::string* insert_path) {
size_t start = 0; size_t start = 0;
bool angle = false; bool angle = false;
@ -73,7 +76,10 @@ bool TrimPath(Project* project, const std::string& project_root, std::string* in
return angle; return angle;
} }
lsCompletionItem BuildCompletionItem(Config* config, const std::string& path, bool use_angle_brackets, bool is_stl) { lsCompletionItem BuildCompletionItem(Config* config,
const std::string& path,
bool use_angle_brackets,
bool is_stl) {
lsCompletionItem item; lsCompletionItem item;
if (use_angle_brackets) if (use_angle_brackets)
item.label = "#include <" + ElideLongPath(config, path) + ">"; item.label = "#include <" + ElideLongPath(config, path) + ">";
@ -111,15 +117,18 @@ void IncludeComplete::Rescan() {
completion_items.clear(); completion_items.clear();
absolute_path_to_completion_item.clear(); absolute_path_to_completion_item.clear();
if (!match_ && (!config_->includeCompletionWhitelist.empty() || !config_->includeCompletionBlacklist.empty())) if (!match_ && (!config_->includeCompletionWhitelist.empty() ||
match_ = MakeUnique<GroupMatch>(config_->includeCompletionWhitelist, config_->includeCompletionBlacklist); !config_->includeCompletionBlacklist.empty()))
match_ = MakeUnique<GroupMatch>(config_->includeCompletionWhitelist,
config_->includeCompletionBlacklist);
is_scanning = true; is_scanning = true;
WorkThread::StartThread("include_scanner", [this]() { WorkThread::StartThread("include_scanner", [this]() {
Timer timer; Timer timer;
InsertStlIncludes(); InsertStlIncludes();
InsertIncludesFromDirectory(config_->projectRoot, false /*use_angle_brackets*/); InsertIncludesFromDirectory(config_->projectRoot,
false /*use_angle_brackets*/);
for (const std::string& dir : project_->quote_include_directories) for (const std::string& dir : project_->quote_include_directories)
InsertIncludesFromDirectory(dir, false /*use_angle_brackets*/); InsertIncludesFromDirectory(dir, false /*use_angle_brackets*/);
for (const std::string& dir : project_->angle_include_directories) for (const std::string& dir : project_->angle_include_directories)
@ -133,48 +142,60 @@ void IncludeComplete::Rescan() {
} }
void IncludeComplete::AddFile(const std::string& absolute_path) { void IncludeComplete::AddFile(const std::string& absolute_path) {
if (!EndsWithAny(absolute_path, config_->includeCompletionWhitelistLiteralEnding)) if (!EndsWithAny(absolute_path,
config_->includeCompletionWhitelistLiteralEnding))
return; return;
if (match_ && !match_->IsMatch(absolute_path)) if (match_ && !match_->IsMatch(absolute_path))
return; return;
std::string trimmed_path = absolute_path; std::string trimmed_path = absolute_path;
bool use_angle_brackets = TrimPath(project_, config_->projectRoot, &trimmed_path); bool use_angle_brackets =
lsCompletionItem item = BuildCompletionItem(config_, trimmed_path, use_angle_brackets, false /*is_stl*/); TrimPath(project_, config_->projectRoot, &trimmed_path);
lsCompletionItem item = BuildCompletionItem(
config_, trimmed_path, use_angle_brackets, false /*is_stl*/);
if (is_scanning) { if (is_scanning) {
std::lock_guard<std::mutex> lock(completion_items_mutex); std::lock_guard<std::mutex> lock(completion_items_mutex);
if (absolute_path_to_completion_item.insert(std::make_pair(absolute_path, completion_items.size())).second) if (absolute_path_to_completion_item
.insert(std::make_pair(absolute_path, completion_items.size()))
.second)
completion_items.push_back(item); completion_items.push_back(item);
} } else {
else { if (absolute_path_to_completion_item
if (absolute_path_to_completion_item.insert(std::make_pair(absolute_path, completion_items.size())).second) .insert(std::make_pair(absolute_path, completion_items.size()))
.second)
completion_items.push_back(item); completion_items.push_back(item);
} }
} }
void IncludeComplete::InsertIncludesFromDirectory( void IncludeComplete::InsertIncludesFromDirectory(std::string directory,
std::string directory, bool use_angle_brackets) {
bool use_angle_brackets) {
directory = NormalizePath(directory); directory = NormalizePath(directory);
EnsureEndsInSlash(directory); EnsureEndsInSlash(directory);
std::vector<CompletionCandidate> results; std::vector<CompletionCandidate> results;
GetFilesInFolder(directory, true /*recursive*/, false /*add_folder_to_path*/, [&](const std::string& path) { GetFilesInFolder(
if (!EndsWithAny(path, config_->includeCompletionWhitelistLiteralEnding)) directory, true /*recursive*/, false /*add_folder_to_path*/,
return; [&](const std::string& path) {
if (match_ && !match_->IsMatch(directory + path)) if (!EndsWithAny(path,
return; config_->includeCompletionWhitelistLiteralEnding))
return;
if (match_ && !match_->IsMatch(directory + path))
return;
CompletionCandidate candidate; CompletionCandidate candidate;
candidate.absolute_path = directory + path; candidate.absolute_path = directory + path;
candidate.completion_item = BuildCompletionItem(config_, path, use_angle_brackets, false /*is_stl*/); candidate.completion_item = BuildCompletionItem(
results.push_back(candidate); config_, path, use_angle_brackets, false /*is_stl*/);
}); results.push_back(candidate);
});
std::lock_guard<std::mutex> lock(completion_items_mutex); std::lock_guard<std::mutex> lock(completion_items_mutex);
for (const CompletionCandidate& result : results) { for (const CompletionCandidate& result : results) {
if (absolute_path_to_completion_item.insert(std::make_pair(result.absolute_path, completion_items.size())).second) if (absolute_path_to_completion_item
.insert(
std::make_pair(result.absolute_path, completion_items.size()))
.second)
completion_items.push_back(result.completion_item); completion_items.push_back(result.completion_item);
} }
} }
@ -182,11 +203,13 @@ void IncludeComplete::InsertIncludesFromDirectory(
void IncludeComplete::InsertStlIncludes() { void IncludeComplete::InsertStlIncludes() {
std::lock_guard<std::mutex> lock(completion_items_mutex); std::lock_guard<std::mutex> lock(completion_items_mutex);
for (const char* stl_header : kStandardLibraryIncludes) { for (const char* stl_header : kStandardLibraryIncludes) {
completion_items.push_back(BuildCompletionItem(config_, stl_header, true /*use_angle_brackets*/, true /*is_stl*/)); completion_items.push_back(BuildCompletionItem(
config_, stl_header, true /*use_angle_brackets*/, true /*is_stl*/));
} }
} }
optional<lsCompletionItem> IncludeComplete::FindCompletionItemForAbsolutePath(const std::string& absolute_path) { optional<lsCompletionItem> IncludeComplete::FindCompletionItemForAbsolutePath(
const std::string& absolute_path) {
std::lock_guard<std::mutex> lock(completion_items_mutex); std::lock_guard<std::mutex> lock(completion_items_mutex);
auto it = absolute_path_to_completion_item.find(absolute_path); auto it = absolute_path_to_completion_item.find(absolute_path);

View File

@ -18,18 +18,20 @@ struct IncludeComplete {
// Ensures the one-off file is inside |completion_items|. // Ensures the one-off file is inside |completion_items|.
void AddFile(const std::string& absolute_path); void AddFile(const std::string& absolute_path);
// Scans the given directory and inserts all includes from this. This is a // Scans the given directory and inserts all includes from this. This is a
// blocking function and should be run off the querydb thread. // blocking function and should be run off the querydb thread.
void InsertIncludesFromDirectory(std::string directory, bool use_angle_brackets); void InsertIncludesFromDirectory(std::string directory,
bool use_angle_brackets);
void InsertStlIncludes(); void InsertStlIncludes();
optional<lsCompletionItem> FindCompletionItemForAbsolutePath(const std::string& absolute_path); optional<lsCompletionItem> FindCompletionItemForAbsolutePath(
const std::string& absolute_path);
// Guards |completion_items| when |is_scanning| is true. // Guards |completion_items| when |is_scanning| is true.
std::mutex completion_items_mutex; std::mutex completion_items_mutex;
std::atomic<bool> is_scanning; std::atomic<bool> is_scanning;
std::vector<lsCompletionItem> completion_items; std::vector<lsCompletionItem> completion_items;
// Absolute file path to the completion item in |completion_items|. Also // Absolute file path to the completion item in |completion_items|. Also
// verifies that we only have one completion item per absolute path. // verifies that we only have one completion item per absolute path.
// We cannot just scan |completion_items| for this information because the // We cannot just scan |completion_items| for this information because the
@ -42,4 +44,3 @@ struct IncludeComplete {
Project* project_; Project* project_;
std::unique_ptr<GroupMatch> match_; std::unique_ptr<GroupMatch> match_;
}; };

View File

@ -14,7 +14,8 @@
#include <algorithm> #include <algorithm>
#include <chrono> #include <chrono>
// TODO: See if we can use clang_indexLoc_getFileLocation to get a type ref on |Foobar| in DISALLOW_COPY(Foobar) // TODO: See if we can use clang_indexLoc_getFileLocation to get a type ref on
// |Foobar| in DISALLOW_COPY(Foobar)
namespace { namespace {
@ -31,13 +32,13 @@ Range Resolve(const CXSourceRange& range, CXFile* cx_file = nullptr) {
CXSourceLocation end = clang_getRangeEnd(range); CXSourceLocation end = clang_getRangeEnd(range);
unsigned int start_line, start_column; unsigned int start_line, start_column;
clang_getSpellingLocation(start, cx_file, &start_line, &start_column, nullptr); clang_getSpellingLocation(start, cx_file, &start_line, &start_column,
nullptr);
unsigned int end_line, end_column; unsigned int end_line, end_column;
clang_getSpellingLocation(end, nullptr, &end_line, &end_column, nullptr); clang_getSpellingLocation(end, nullptr, &end_line, &end_column, nullptr);
return Range( return Range(Position((int16_t)start_line, (int16_t)start_column) /*start*/,
Position((int16_t)start_line, (int16_t)start_column) /*start*/, Position((int16_t)end_line, (int16_t)end_column) /*end*/);
Position((int16_t)end_line, (int16_t)end_column) /*end*/);
} }
Range ResolveSpelling(const CXCursor& cx_cursor, CXFile* cx_file = nullptr) { Range ResolveSpelling(const CXCursor& cx_cursor, CXFile* cx_file = nullptr) {
@ -50,19 +51,18 @@ Range ResolveExtent(const CXCursor& cx_cursor, CXFile* cx_file = nullptr) {
return Resolve(cx_range, cx_file); return Resolve(cx_range, cx_file);
} }
struct NamespaceHelper { struct NamespaceHelper {
std::unordered_map<std::string, std::string> container_usr_to_qualified_name; std::unordered_map<std::string, std::string> container_usr_to_qualified_name;
void RegisterQualifiedName(std::string usr, void RegisterQualifiedName(std::string usr,
const CXIdxContainerInfo* container, const CXIdxContainerInfo* container,
std::string qualified_name) { std::string qualified_name) {
if (container) { if (container) {
std::string container_usr = clang::Cursor(container->cursor).get_usr(); std::string container_usr = clang::Cursor(container->cursor).get_usr();
auto it = container_usr_to_qualified_name.find(container_usr); auto it = container_usr_to_qualified_name.find(container_usr);
if (it != container_usr_to_qualified_name.end()) { if (it != container_usr_to_qualified_name.end()) {
container_usr_to_qualified_name[usr] = container_usr_to_qualified_name[usr] =
it->second + qualified_name + "::"; it->second + qualified_name + "::";
return; return;
} }
} }
@ -71,7 +71,7 @@ struct NamespaceHelper {
} }
std::string QualifiedName(const CXIdxContainerInfo* container, std::string QualifiedName(const CXIdxContainerInfo* container,
std::string unqualified_name) { std::string unqualified_name) {
if (container) { if (container) {
std::string container_usr = clang::Cursor(container->cursor).get_usr(); std::string container_usr = clang::Cursor(container->cursor).get_usr();
auto it = container_usr_to_qualified_name.find(container_usr); auto it = container_usr_to_qualified_name.find(container_usr);
@ -109,12 +109,14 @@ struct IndexParam {
FileConsumer* file_consumer = nullptr; FileConsumer* file_consumer = nullptr;
NamespaceHelper ns; NamespaceHelper ns;
IndexParam(clang::TranslationUnit* tu, FileConsumer* file_consumer) : tu(tu), file_consumer(file_consumer) {} IndexParam(clang::TranslationUnit* tu, FileConsumer* file_consumer)
: tu(tu), file_consumer(file_consumer) {}
}; };
IndexFile* ConsumeFile(IndexParam* param, CXFile file) { IndexFile* ConsumeFile(IndexParam* param, CXFile file) {
bool is_first_ownership = false; bool is_first_ownership = false;
IndexFile* db = param->file_consumer->TryConsumeFile(file, &is_first_ownership); IndexFile* db =
param->file_consumer->TryConsumeFile(file, &is_first_ownership);
// If this is the first time we have seen the file (ignoring if we are // If this is the first time we have seen the file (ignoring if we are
// generating an index for it): // generating an index for it):
@ -129,18 +131,21 @@ IndexFile* ConsumeFile(IndexParam* param, CXFile file) {
// Set modification time. // Set modification time.
optional<int64_t> modification_time = GetLastModificationTime(file_name); optional<int64_t> modification_time = GetLastModificationTime(file_name);
LOG_IF_S(ERROR, !modification_time) << "Failed fetching modification time for " << file_name; LOG_IF_S(ERROR, !modification_time)
<< "Failed fetching modification time for " << file_name;
if (modification_time) if (modification_time)
param->file_modification_times[file_name] = *modification_time; param->file_modification_times[file_name] = *modification_time;
// Capture file contents in |param->file_contents| if it was not specified // Capture file contents in |param->file_contents| if it was not specified
// at the start of indexing. // at the start of indexing.
if (db && param->file_contents.find(file_name) == param->file_contents.end()) { if (db &&
param->file_contents.find(file_name) == param->file_contents.end()) {
optional<std::string> content = ReadContent(file_name); optional<std::string> content = ReadContent(file_name);
if (content) if (content)
param->file_contents[file_name] = *content; param->file_contents[file_name] = *content;
else else
LOG_S(ERROR) << "[indexer] Failed to read file content for " << file_name; LOG_S(ERROR) << "[indexer] Failed to read file content for "
<< file_name;
} }
} }
} }
@ -179,18 +184,20 @@ bool IsLocalSemanticContainer(CXCursorKind kind) {
// actually being written in the source code. // actually being written in the source code.
bool CanBeCalledImplicitly(CXIdxEntityKind kind) { bool CanBeCalledImplicitly(CXIdxEntityKind kind) {
switch (kind) { switch (kind) {
case CXIdxEntity_CXXConstructor: case CXIdxEntity_CXXConstructor:
case CXIdxEntity_CXXConversionFunction: case CXIdxEntity_CXXConversionFunction:
case CXIdxEntity_CXXDestructor: case CXIdxEntity_CXXDestructor:
return true; return true;
default: default:
return false; return false;
} }
} }
// Returns true if the cursor spelling contains the given string. This is // Returns true if the cursor spelling contains the given string. This is
// useful to check for implicit function calls. // useful to check for implicit function calls.
bool CursorSpellingContainsString(CXCursor cursor, CXTranslationUnit cx_tu, std::string scanning_for) { bool CursorSpellingContainsString(CXCursor cursor,
CXTranslationUnit cx_tu,
std::string scanning_for) {
CXSourceRange range = clang_Cursor_getSpellingNameRange(cursor, 0, 0); CXSourceRange range = clang_Cursor_getSpellingNameRange(cursor, 0, 0);
CXToken* tokens; CXToken* tokens;
unsigned num_tokens; unsigned num_tokens;
@ -213,7 +220,8 @@ bool CursorSpellingContainsString(CXCursor cursor, CXTranslationUnit cx_tu, std:
// Returns the document content for the given range. May not work perfectly // Returns the document content for the given range. May not work perfectly
// when there are tabs instead of spaces. // when there are tabs instead of spaces.
std::string GetDocumentContentInRange(CXTranslationUnit cx_tu, CXSourceRange range) { std::string GetDocumentContentInRange(CXTranslationUnit cx_tu,
CXSourceRange range) {
std::string result; std::string result;
CXToken* tokens; CXToken* tokens;
@ -227,7 +235,8 @@ std::string GetDocumentContentInRange(CXTranslationUnit cx_tu, CXSourceRange ran
Range token_range = Resolve(clang_getTokenExtent(cx_tu, tokens[i])); Range token_range = Resolve(clang_getTokenExtent(cx_tu, tokens[i]));
if (previous_token_range) { if (previous_token_range) {
// Insert newlines. // Insert newlines.
int16_t line_delta = token_range.start.line - previous_token_range->end.line; int16_t line_delta =
token_range.start.line - previous_token_range->end.line;
assert(line_delta >= 0); assert(line_delta >= 0);
if (line_delta > 0) { if (line_delta > 0) {
result.append((size_t)line_delta, '\n'); result.append((size_t)line_delta, '\n');
@ -235,7 +244,8 @@ std::string GetDocumentContentInRange(CXTranslationUnit cx_tu, CXSourceRange ran
previous_token_range->end.column = 0; previous_token_range->end.column = 0;
} }
// Insert spaces. // Insert spaces.
int16_t column_delta = token_range.start.column - previous_token_range->end.column; int16_t column_delta =
token_range.start.column - previous_token_range->end.column;
assert(column_delta >= 0); assert(column_delta >= 0);
result.append((size_t)column_delta, ' '); result.append((size_t)column_delta, ' ');
} }
@ -381,7 +391,7 @@ void UniqueAdd(std::vector<T>& values, T value) {
} }
IdCache::IdCache(const std::string& primary_file) IdCache::IdCache(const std::string& primary_file)
: primary_file(primary_file) {} : primary_file(primary_file) {}
template <typename T> template <typename T>
bool Contains(const std::vector<T>& vec, const T& element) { bool Contains(const std::vector<T>& vec, const T& element) {
@ -640,7 +650,6 @@ optional<IndexTypeId> AddDeclTypeUsages(
clang::Cursor decl_cursor, clang::Cursor decl_cursor,
const CXIdxContainerInfo* semantic_container, const CXIdxContainerInfo* semantic_container,
const CXIdxContainerInfo* lexical_container) { const CXIdxContainerInfo* lexical_container) {
// std::cerr << std::endl << "AddDeclUsages " << decl_cursor.get_spelling() << // std::cerr << std::endl << "AddDeclUsages " << decl_cursor.get_spelling() <<
// std::endl; // std::endl;
// Dump(decl_cursor); // Dump(decl_cursor);
@ -838,20 +847,20 @@ bool AreEqualLocations(CXIdxLoc loc, CXCursor cursor) {
// clang_Cursor_getSpellingNameRange // clang_Cursor_getSpellingNameRange
return clang_equalLocations( return clang_equalLocations(
clang_indexLoc_getCXSourceLocation(loc), clang_indexLoc_getCXSourceLocation(loc),
//clang_getRangeStart(clang_getCursorExtent(cursor))); // clang_getRangeStart(clang_getCursorExtent(cursor)));
clang_getRangeStart(clang_Cursor_getSpellingNameRange(cursor, 0, 0))); clang_getRangeStart(clang_Cursor_getSpellingNameRange(cursor, 0, 0)));
} }
clang::VisiterResult VisitMacroDefinitionAndExpansions(clang::Cursor cursor,
clang::Cursor parent,
IndexParam* param) {
clang::VisiterResult VisitMacroDefinitionAndExpansions(clang::Cursor cursor, clang::Cursor parent, IndexParam* param) {
switch (cursor.get_kind()) { switch (cursor.get_kind()) {
case CXCursor_MacroDefinition: case CXCursor_MacroDefinition:
case CXCursor_MacroExpansion: { case CXCursor_MacroExpansion: {
// Resolve location, find IndexFile instance. // Resolve location, find IndexFile instance.
CXSourceRange cx_source_range = clang_Cursor_getSpellingNameRange(cursor.cx_cursor, 0, 0); CXSourceRange cx_source_range =
clang_Cursor_getSpellingNameRange(cursor.cx_cursor, 0, 0);
CXSourceLocation start = clang_getRangeStart(cx_source_range); CXSourceLocation start = clang_getRangeStart(cx_source_range);
if (clang_Location_isInSystemHeader(start)) if (clang_Location_isInSystemHeader(start))
break; break;
@ -881,7 +890,8 @@ clang::VisiterResult VisitMacroDefinitionAndExpansions(clang::Cursor cursor, cla
var_def->def.is_local = false; var_def->def.is_local = false;
var_def->def.is_macro = true; var_def->def.is_macro = true;
var_def->def.definition_spelling = decl_loc_spelling; var_def->def.definition_spelling = decl_loc_spelling;
var_def->def.definition_extent = ResolveExtent(cursor.cx_cursor);; var_def->def.definition_extent = ResolveExtent(cursor.cx_cursor);
;
} }
break; break;
@ -945,13 +955,16 @@ clang::VisiterResult VisitMacroDefinitionAndExpansions(clang::Cursor cursor, cla
void indexDeclaration(CXClientData client_data, const CXIdxDeclInfo* decl) { void indexDeclaration(CXClientData client_data, const CXIdxDeclInfo* decl) {
if (!kIndexStdDeclarations && clang_Location_isInSystemHeader(clang_indexLoc_getCXSourceLocation(decl->loc))) if (!kIndexStdDeclarations &&
clang_Location_isInSystemHeader(
clang_indexLoc_getCXSourceLocation(decl->loc)))
return; return;
assert(AreEqualLocations(decl->loc, decl->cursor)); assert(AreEqualLocations(decl->loc, decl->cursor));
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);
IndexFile* db = ConsumeFile(param, file); IndexFile* db = ConsumeFile(param, file);
if (!db) if (!db)
@ -959,8 +972,9 @@ void indexDeclaration(CXClientData client_data, const CXIdxDeclInfo* decl) {
NamespaceHelper* ns = &param->ns; NamespaceHelper* ns = &param->ns;
// std::cerr << "DECL kind=" << decl->entityInfo->kind << " at " <<
//std::cerr << "DECL kind=" << decl->entityInfo->kind << " at " << db->id_cache.Resolve(decl->cursor, false).ToPrettyString(&db->id_cache) << std::endl; // db->id_cache.Resolve(decl->cursor, false).ToPrettyString(&db->id_cache) <<
// std::endl;
switch (decl->entityInfo->kind) { switch (decl->entityInfo->kind) {
case CXIdxEntity_CXXNamespace: { case CXIdxEntity_CXXNamespace: {
@ -993,18 +1007,23 @@ void indexDeclaration(CXClientData client_data, const CXIdxDeclInfo* decl) {
// if (!decl->isRedeclaration) { // if (!decl->isRedeclaration) {
var->def.short_name = decl->entityInfo->name; var->def.short_name = decl->entityInfo->name;
std::string type_name = clang::ToString(clang_getTypeSpelling(clang_getCursorType(decl->cursor))); std::string type_name = clang::ToString(
var->def.detailed_name = type_name + " " + ns->QualifiedName(decl->semanticContainer, var->def.short_name); clang_getTypeSpelling(clang_getCursorType(decl->cursor)));
var->def.detailed_name =
type_name + " " +
ns->QualifiedName(decl->semanticContainer, var->def.short_name);
var->def.is_local = !decl->semanticContainer || IsLocalSemanticContainer(decl->semanticContainer->cursor.kind); var->def.is_local =
!decl->semanticContainer ||
IsLocalSemanticContainer(decl->semanticContainer->cursor.kind);
//} //}
if (decl->isDefinition) { if (decl->isDefinition) {
var->def.definition_spelling = ResolveSpelling(decl->cursor); var->def.definition_spelling = ResolveSpelling(decl->cursor);
var->def.definition_extent = ResolveExtent(decl->cursor);; var->def.definition_extent = ResolveExtent(decl->cursor);
} ;
else { } else {
var->def.declaration = ResolveSpelling(decl->cursor); var->def.declaration = ResolveSpelling(decl->cursor);
} }
UniqueAdd(var->uses, decl_loc_spelling); UniqueAdd(var->uses, decl_loc_spelling);
@ -1020,17 +1039,19 @@ void indexDeclaration(CXClientData client_data, const CXIdxDeclInfo* decl) {
// the function declaration is encountered since we won't receive ParmDecl // the function declaration is encountered since we won't receive ParmDecl
// declarations for unnamed parameters. // declarations for unnamed parameters.
// TODO: See if we can remove this function call. // TODO: See if we can remove this function call.
AddDeclTypeUsages( AddDeclTypeUsages(db, decl_cursor, decl->semanticContainer,
db, decl_cursor, decl->lexicalContainer);
decl->semanticContainer, decl->lexicalContainer);
// We don't need to assign declaring type multiple times if this variable // We don't need to assign declaring type multiple times if this variable
// has already been seen. // has already been seen.
if (!decl->isRedeclaration) { if (!decl->isRedeclaration) {
optional<IndexTypeId> var_type = ResolveToDeclarationType(db, decl_cursor); optional<IndexTypeId> var_type =
ResolveToDeclarationType(db, decl_cursor);
if (var_type.has_value()) { if (var_type.has_value()) {
// Don't treat enum definition variables as instantiations. // Don't treat enum definition variables as instantiations.
bool is_enum_member = decl->semanticContainer && decl->semanticContainer->cursor.kind == CXCursor_EnumDecl; bool is_enum_member =
decl->semanticContainer &&
decl->semanticContainer->cursor.kind == CXCursor_EnumDecl;
if (!is_enum_member) if (!is_enum_member)
db->Resolve(var_type.value())->instances.push_back(var_id); db->Resolve(var_type.value())->instances.push_back(var_id);
@ -1038,10 +1059,11 @@ void indexDeclaration(CXClientData client_data, const CXIdxDeclInfo* decl) {
} }
} }
// TODO: Refactor handlers so more things are under 'if (!decl->isRedeclaration)' // TODO: Refactor handlers so more things are under 'if
// (!decl->isRedeclaration)'
if (decl->isDefinition && IsTypeDefinition(decl->semanticContainer)) { if (decl->isDefinition && IsTypeDefinition(decl->semanticContainer)) {
IndexTypeId declaring_type_id = IndexTypeId declaring_type_id =
db->ToTypeId(decl->semanticContainer->cursor); db->ToTypeId(decl->semanticContainer->cursor);
IndexType* declaring_type_def = db->Resolve(declaring_type_id); IndexType* declaring_type_def = db->Resolve(declaring_type_id);
var->def.declaring_type = declaring_type_id; var->def.declaring_type = declaring_type_id;
declaring_type_def->def.vars.push_back(var_id); declaring_type_def->def.vars.push_back(var_id);
@ -1060,7 +1082,8 @@ void indexDeclaration(CXClientData client_data, const CXIdxDeclInfo* decl) {
Range decl_extent = ResolveExtent(decl->cursor); Range decl_extent = ResolveExtent(decl->cursor);
clang::Cursor decl_cursor = decl->cursor; clang::Cursor decl_cursor = decl->cursor;
clang::Cursor decl_cursor_resolved = decl_cursor.template_specialization_to_template_definition(); clang::Cursor decl_cursor_resolved =
decl_cursor.template_specialization_to_template_definition();
bool is_template_specialization = decl_cursor != decl_cursor_resolved; bool is_template_specialization = decl_cursor != decl_cursor_resolved;
IndexFuncId func_id = db->ToFuncId(decl_cursor_resolved.cx_cursor); IndexFuncId func_id = db->ToFuncId(decl_cursor_resolved.cx_cursor);
@ -1068,8 +1091,8 @@ void indexDeclaration(CXClientData client_data, const CXIdxDeclInfo* decl) {
// We don't actually need to know the return type, but we need to mark it // We don't actually need to know the return type, but we need to mark it
// as an interesting usage. // as an interesting usage.
AddDeclTypeUsages(db, decl_cursor, AddDeclTypeUsages(db, decl_cursor, decl->semanticContainer,
decl->semanticContainer, decl->lexicalContainer); decl->lexicalContainer);
// Add definition or declaration. This is a bit tricky because we treat // Add definition or declaration. This is a bit tricky because we treat
// template specializations as declarations, even though they are // template specializations as declarations, even though they are
@ -1077,16 +1100,16 @@ void indexDeclaration(CXClientData client_data, const CXIdxDeclInfo* decl) {
// TODO: Support multiple function definitions, which is common for // TODO: Support multiple function definitions, which is common for
// template specializations. // template specializations.
if (decl->isDefinition && !is_template_specialization) { if (decl->isDefinition && !is_template_specialization) {
//assert(!func->def.definition_spelling); // assert(!func->def.definition_spelling);
//assert(!func->def.definition_extent); // assert(!func->def.definition_extent);
func->def.definition_spelling = decl_spelling; func->def.definition_spelling = decl_spelling;
func->def.definition_extent = decl_extent; func->def.definition_extent = decl_extent;
} } else {
else {
IndexFunc::Declaration declaration; IndexFunc::Declaration declaration;
declaration.spelling = decl_spelling; declaration.spelling = decl_spelling;
declaration.extent = decl_extent; declaration.extent = decl_extent;
declaration.content = GetDocumentContentInRange(param->tu->cx_tu, clang_getCursorExtent(decl->cursor)); declaration.content = GetDocumentContentInRange(
param->tu->cx_tu, clang_getCursorExtent(decl->cursor));
// Add parameters. // Add parameters.
for (clang::Cursor arg : decl_cursor.get_arguments()) { for (clang::Cursor arg : decl_cursor.get_arguments()) {
@ -1096,7 +1119,8 @@ void indexDeclaration(CXClientData client_data, const CXIdxDeclInfo* decl) {
// If the name is empty (which is common for parameters), clang // If the name is empty (which is common for parameters), clang
// will report a range with length 1, which is not correct. // will report a range with length 1, which is not correct.
if (param_spelling.start.column == (param_spelling.end.column - 1) && if (param_spelling.start.column ==
(param_spelling.end.column - 1) &&
arg.get_display_name().empty()) { arg.get_display_name().empty()) {
param_spelling.end.column -= 1; param_spelling.end.column -= 1;
} }
@ -1122,7 +1146,8 @@ void indexDeclaration(CXClientData client_data, const CXIdxDeclInfo* decl) {
// Build detailed name. The type desc looks like void (void *). We // Build detailed name. The type desc looks like void (void *). We
// insert the qualified name before the first '('. // insert the qualified name before the first '('.
std::string qualified_name = ns->QualifiedName(decl->semanticContainer, func->def.short_name); std::string qualified_name =
ns->QualifiedName(decl->semanticContainer, func->def.short_name);
std::string type_desc = decl_cursor.get_type_description(); std::string type_desc = decl_cursor.get_type_description();
size_t offset = type_desc.find('('); size_t offset = type_desc.find('(');
type_desc.insert(offset, qualified_name); type_desc.insert(offset, qualified_name);
@ -1142,7 +1167,7 @@ void indexDeclaration(CXClientData client_data, const CXIdxDeclInfo* decl) {
UniqueAdd(declaring_type_def->uses, decl_spelling); UniqueAdd(declaring_type_def->uses, decl_spelling);
if (decl->entityInfo->kind == CXIdxEntity_CXXDestructor) { if (decl->entityInfo->kind == CXIdxEntity_CXXDestructor) {
Range dtor_type_range = decl_spelling; Range dtor_type_range = decl_spelling;
dtor_type_range.start.column += 1; // Don't count the leading ~ dtor_type_range.start.column += 1; // Don't count the leading ~
UniqueAdd(declaring_type_def->uses, dtor_type_range); UniqueAdd(declaring_type_def->uses, dtor_type_range);
} }
@ -1155,12 +1180,13 @@ void indexDeclaration(CXClientData client_data, const CXIdxDeclInfo* decl) {
CXCursor* overridden; CXCursor* overridden;
unsigned int num_overridden; unsigned int num_overridden;
clang_getOverriddenCursors(decl->cursor, &overridden, clang_getOverriddenCursors(decl->cursor, &overridden,
&num_overridden); &num_overridden);
// FIXME if it ever shows up. Methods should only ever have 1 base // FIXME if it ever shows up. Methods should only ever have 1 base
// type, though. // type, though.
if (num_overridden > 1) if (num_overridden > 1)
std::cerr << "[indexer]: warning: multiple base overrides for " << func->def.detailed_name << std::endl; std::cerr << "[indexer]: warning: multiple base overrides for "
<< func->def.detailed_name << std::endl;
for (unsigned i = 0; i < num_overridden; ++i) { for (unsigned i = 0; i < num_overridden; ++i) {
clang::Cursor parent = overridden[i]; clang::Cursor parent = overridden[i];
@ -1185,9 +1211,8 @@ void indexDeclaration(CXClientData client_data, const CXIdxDeclInfo* decl) {
// Note we want to fetch the first TypeRef. Running // Note we want to fetch the first TypeRef. Running
// ResolveCursorType(decl->cursor) would return // ResolveCursorType(decl->cursor) would return
// the type of the typedef/using, not the type of the referenced type. // the type of the typedef/using, not the type of the referenced type.
optional<IndexTypeId> alias_of = optional<IndexTypeId> alias_of = AddDeclTypeUsages(
AddDeclTypeUsages(db, decl->cursor, db, decl->cursor, decl->semanticContainer, decl->lexicalContainer);
decl->semanticContainer, decl->lexicalContainer);
IndexTypeId type_id = db->ToTypeId(decl->entityInfo->USR); IndexTypeId type_id = db->ToTypeId(decl->entityInfo->USR);
IndexType* type = db->Resolve(type_id); IndexType* type = db->Resolve(type_id);
@ -1254,15 +1279,14 @@ void indexDeclaration(CXClientData client_data, const CXIdxDeclInfo* decl) {
for (unsigned int i = 0; i < class_info->numBases; ++i) { for (unsigned int i = 0; i < class_info->numBases; ++i) {
const CXIdxBaseClassInfo* base_class = class_info->bases[i]; const CXIdxBaseClassInfo* base_class = class_info->bases[i];
AddDeclTypeUsages(db, base_class->cursor, AddDeclTypeUsages(db, base_class->cursor, decl->semanticContainer,
decl->semanticContainer, decl->lexicalContainer); decl->lexicalContainer);
optional<IndexTypeId> parent_type_id = optional<IndexTypeId> parent_type_id =
ResolveToDeclarationType(db, base_class->cursor); ResolveToDeclarationType(db, base_class->cursor);
// type_def ptr could be invalidated by ResolveToDeclarationType. // type_def ptr could be invalidated by ResolveToDeclarationType.
type = db->Resolve(type_id); type = db->Resolve(type_id);
if (parent_type_id) { if (parent_type_id) {
IndexType* parent_type_def = IndexType* parent_type_def = db->Resolve(parent_type_id.value());
db->Resolve(parent_type_id.value());
parent_type_def->derived.push_back(type_id); parent_type_def->derived.push_back(type_id);
type->def.parents.push_back(parent_type_id.value()); type->def.parents.push_back(parent_type_id.value());
} }
@ -1272,11 +1296,9 @@ void indexDeclaration(CXClientData client_data, const CXIdxDeclInfo* decl) {
} }
default: default:
std::cerr std::cerr << "!! Unhandled indexDeclaration: "
<< "!! Unhandled indexDeclaration: " << clang::Cursor(decl->cursor).ToString() << " at "
<< clang::Cursor(decl->cursor).ToString() << " at " << ResolveSpelling(decl->cursor).start.ToString() << std::endl;
<< ResolveSpelling(decl->cursor).start.ToString()
<< std::endl;
std::cerr << " entityInfo->kind = " << decl->entityInfo->kind std::cerr << " entityInfo->kind = " << decl->entityInfo->kind
<< std::endl; << std::endl;
std::cerr << " entityInfo->USR = " << decl->entityInfo->USR std::cerr << " entityInfo->USR = " << decl->entityInfo->USR
@ -1367,20 +1389,24 @@ bool IsFunctionCallContext(CXCursorKind kind) {
void indexEntityReference(CXClientData client_data, void indexEntityReference(CXClientData client_data,
const CXIdxEntityRefInfo* ref) { const CXIdxEntityRefInfo* ref) {
// Don't index references from or to system headers. // Don't index references from or to system headers.
if (clang_Location_isInSystemHeader(clang_indexLoc_getCXSourceLocation(ref->loc)) || if (clang_Location_isInSystemHeader(
clang_Location_isInSystemHeader(clang_getCursorLocation(ref->referencedEntity->cursor))) clang_indexLoc_getCXSourceLocation(ref->loc)) ||
clang_Location_isInSystemHeader(
clang_getCursorLocation(ref->referencedEntity->cursor)))
return; return;
//assert(AreEqualLocations(ref->loc, ref->cursor)); // assert(AreEqualLocations(ref->loc, ref->cursor));
// if (clang_Location_isInSystemHeader(clang_getCursorLocation(ref->cursor)) || // if (clang_Location_isInSystemHeader(clang_getCursorLocation(ref->cursor))
// clang_Location_isInSystemHeader( // ||
// clang_getCursorLocation(ref->referencedEntity->cursor))) // clang_Location_isInSystemHeader(
// return; // clang_getCursorLocation(ref->referencedEntity->cursor)))
// return;
// TODO: Use clang_getFileUniqueID // TODO: Use clang_getFileUniqueID
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);
IndexFile* db = ConsumeFile(param, file); IndexFile* db = ConsumeFile(param, file);
if (!db) if (!db)
@ -1390,17 +1416,18 @@ void indexEntityReference(CXClientData client_data,
// ref->loc mainFile=1 // ref->loc mainFile=1
// ref->referencedEntity mainFile=1 // ref->referencedEntity mainFile=1
// //
// Regardless, we need to do more advanced location processing to handle multiple output IndexFile instances. // Regardless, we need to do more advanced location processing to handle
//bool mainFile = clang_Location_isFromMainFile(clang_indexLoc_getCXSourceLocation(ref->loc)); // multiple output IndexFile instances.
//Range loc_spelling = param->db->id_cache.ForceResolveSpelling(ref->cursor, false /*interesting*/); // bool mainFile =
//std::cerr << "mainFile: " << mainFile << ", loc: " << loc_spelling.ToString() << std::endl; // clang_Location_isFromMainFile(clang_indexLoc_getCXSourceLocation(ref->loc));
// Range loc_spelling = param->db->id_cache.ForceResolveSpelling(ref->cursor,
// false /*interesting*/); std::cerr << "mainFile: " << mainFile << ", loc: "
// << loc_spelling.ToString() << std::endl;
// Don't index references that are not from the main file. // Don't index references that are not from the main file.
//if (!clang_Location_isFromMainFile(clang_getCursorLocation(ref->cursor))) // if (!clang_Location_isFromMainFile(clang_getCursorLocation(ref->cursor)))
// return; // return;
clang::Cursor cursor(ref->cursor); clang::Cursor cursor(ref->cursor);
// std::cerr << "REF kind=" << ref->referencedEntity->kind << " at " << // std::cerr << "REF kind=" << ref->referencedEntity->kind << " at " <<
@ -1453,8 +1480,10 @@ void indexEntityReference(CXClientData client_data,
// libclang doesn't provide a nice api to check if the given function // libclang doesn't provide a nice api to check if the given function
// call is implicit. ref->kind should probably work (it's either direct // call is implicit. ref->kind should probably work (it's either direct
// or implicit), but libclang only supports implicit for objective-c. // or implicit), but libclang only supports implicit for objective-c.
bool is_implicit = CanBeCalledImplicitly(ref->referencedEntity->kind) && bool is_implicit =
!CursorSpellingContainsString(ref->cursor, param->tu->cx_tu, called->def.short_name); CanBeCalledImplicitly(ref->referencedEntity->kind) &&
!CursorSpellingContainsString(ref->cursor, param->tu->cx_tu,
called->def.short_name);
if (IsFunctionCallContext(ref->container->cursor.kind)) { if (IsFunctionCallContext(ref->container->cursor.kind)) {
IndexFuncId caller_id = db->ToFuncId(ref->container->cursor); IndexFuncId caller_id = db->ToFuncId(ref->container->cursor);
@ -1462,8 +1491,10 @@ void indexEntityReference(CXClientData client_data,
// Calling db->ToFuncId invalidates the FuncDef* ptrs. // Calling db->ToFuncId invalidates the FuncDef* ptrs.
called = db->Resolve(called_id); called = db->Resolve(called_id);
AddFuncRef(&caller->def.callees, IndexFuncRef(called_id, loc_spelling, is_implicit)); AddFuncRef(&caller->def.callees,
AddFuncRef(&called->callers, IndexFuncRef(caller_id, loc_spelling, is_implicit)); IndexFuncRef(called_id, loc_spelling, is_implicit));
AddFuncRef(&called->callers,
IndexFuncRef(caller_id, loc_spelling, is_implicit));
} else { } else {
AddFuncRef(&called->callers, IndexFuncRef(loc_spelling, is_implicit)); AddFuncRef(&called->callers, IndexFuncRef(loc_spelling, is_implicit));
} }
@ -1478,7 +1509,8 @@ void indexEntityReference(CXClientData client_data,
case CXIdxEntity_Struct: case CXIdxEntity_Struct:
case CXIdxEntity_CXXClass: { case CXIdxEntity_CXXClass: {
clang::Cursor referenced_cursor = ref->referencedEntity->cursor; clang::Cursor referenced_cursor = ref->referencedEntity->cursor;
referenced_cursor = referenced_cursor.template_specialization_to_template_definition(); referenced_cursor =
referenced_cursor.template_specialization_to_template_definition();
IndexTypeId referenced_id = db->ToTypeId(referenced_cursor.get_usr()); IndexTypeId referenced_id = db->ToTypeId(referenced_cursor.get_usr());
IndexType* referenced = db->Resolve(referenced_id); IndexType* referenced = db->Resolve(referenced_id);
@ -1503,20 +1535,16 @@ void indexEntityReference(CXClientData client_data,
} }
default: default:
std::cerr std::cerr << "!! Unhandled indexEntityReference: " << cursor.ToString()
<< "!! Unhandled indexEntityReference: " << cursor.ToString() << " at " << ResolveSpelling(ref->cursor).start.ToString()
<< " at " << std::endl;
<< ResolveSpelling(ref->cursor).start.ToString()
<< std::endl;
std::cerr << " ref->referencedEntity->kind = " std::cerr << " ref->referencedEntity->kind = "
<< ref->referencedEntity->kind << std::endl; << ref->referencedEntity->kind << std::endl;
if (ref->parentEntity) if (ref->parentEntity)
std::cerr << " ref->parentEntity->kind = " std::cerr << " ref->parentEntity->kind = "
<< ref->parentEntity->kind << std::endl; << ref->parentEntity->kind << std::endl;
std::cerr std::cerr << " ref->loc = "
<< " ref->loc = " << ResolveSpelling(ref->cursor).start.ToString() << std::endl;
<< ResolveSpelling(ref->cursor).start.ToString()
<< std::endl;
std::cerr << " ref->kind = " << ref->kind << std::endl; std::cerr << " ref->kind = " << ref->kind << std::endl;
if (ref->parentEntity) if (ref->parentEntity)
std::cerr << " parentEntity = " std::cerr << " parentEntity = "
@ -1563,17 +1591,18 @@ void indexEntityReference(CXClientData client_data,
FileContents::FileContents(const std::string& path, const std::string& content) : path(path), content(content) {} FileContents::FileContents(const std::string& path, const std::string& content)
: path(path), content(content) {}
std::vector<std::unique_ptr<IndexFile>> Parse( std::vector<std::unique_ptr<IndexFile>> Parse(
Config* config, FileConsumer::SharedState* file_consumer_shared, Config* config,
FileConsumer::SharedState* file_consumer_shared,
std::string file, std::string file,
std::vector<std::string> args, std::vector<std::string> args,
std::vector<FileContents> file_contents, std::vector<FileContents> file_contents,
PerformanceImportFile* perf, PerformanceImportFile* perf,
clang::Index* index, clang::Index* index,
bool dump_ast) { bool dump_ast) {
if (!config->enableIndexing) if (!config->enableIndexing)
return {}; return {};
@ -1590,9 +1619,12 @@ std::vector<std::unique_ptr<IndexFile>> Parse(
unsaved_files.push_back(unsaved); unsaved_files.push_back(unsaved);
} }
clang::TranslationUnit tu(index, file, args, unsaved_files, CXTranslationUnit_KeepGoing | CXTranslationUnit_DetailedPreprocessingRecord); clang::TranslationUnit tu(index, file, args, unsaved_files,
CXTranslationUnit_KeepGoing |
CXTranslationUnit_DetailedPreprocessingRecord);
if (tu.did_fail) { if (tu.did_fail) {
std::cerr << "!! Failed creating translation unit for " << file << std::endl; std::cerr << "!! Failed creating translation unit for " << file
<< std::endl;
return {}; return {};
} }
@ -1601,12 +1633,10 @@ std::vector<std::unique_ptr<IndexFile>> Parse(
if (dump_ast) if (dump_ast)
Dump(tu.document_cursor()); Dump(tu.document_cursor());
IndexerCallbacks callbacks[] = {{&abortQuery, &diagnostic, &enteredMainFile,
IndexerCallbacks callbacks[] = { &ppIncludedFile, &importedASTFile,
{&abortQuery, &diagnostic, &enteredMainFile, &ppIncludedFile, &startedTranslationUnit, &indexDeclaration,
&importedASTFile, &startedTranslationUnit, &indexDeclaration, &indexEntityReference}};
&indexEntityReference}
};
FileConsumer file_consumer(file_consumer_shared, file); FileConsumer file_consumer(file_consumer_shared, file);
IndexParam param(&tu, &file_consumer); IndexParam param(&tu, &file_consumer);
@ -1617,16 +1647,19 @@ std::vector<std::unique_ptr<IndexFile>> Parse(
CXFile cx_file = clang_getFile(tu.cx_tu, file.c_str()); CXFile cx_file = clang_getFile(tu.cx_tu, file.c_str());
param.primary_file = ConsumeFile(&param, cx_file); param.primary_file = ConsumeFile(&param, cx_file);
//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);
clang_indexTranslationUnit(index_action, &param, callbacks, sizeof(callbacks), clang_indexTranslationUnit(index_action, &param, callbacks, sizeof(callbacks),
CXIndexOpt_IndexFunctionLocalSymbols | CXIndexOpt_SkipParsedBodiesInSession | CXIndexOpt_IndexImplicitTemplateInstantiations, CXIndexOpt_IndexFunctionLocalSymbols |
CXIndexOpt_SkipParsedBodiesInSession |
CXIndexOpt_IndexImplicitTemplateInstantiations,
tu.cx_tu); tu.cx_tu);
clang_IndexAction_dispose(index_action); clang_IndexAction_dispose(index_action);
//std::cerr << "!! [END] Indexing " << file << std::endl; // std::cerr << "!! [END] Indexing " << file << std::endl;
tu.document_cursor().VisitChildren(&VisitMacroDefinitionAndExpansions, &param); tu.document_cursor().VisitChildren(&VisitMacroDefinitionAndExpansions,
&param);
perf->index_build = timer.ElapsedMicrosecondsAndReset(); perf->index_build = timer.ElapsedMicrosecondsAndReset();
@ -1655,7 +1688,6 @@ std::vector<std::unique_ptr<IndexFile>> Parse(
return result; return result;
} }
void IndexInit() { void IndexInit() {
clang_enableStackTraces(); clang_enableStackTraces();
clang_toggleCrashRecovery(1); clang_toggleCrashRecovery(1);

View File

@ -1,28 +1,31 @@
#pragma once #pragma once
#include "file_consumer.h" #include "file_consumer.h"
#include "position.h"
#include "serializer.h"
#include "utils.h"
#include "language_server_api.h" #include "language_server_api.h"
#include "libclangmm/Index.h" #include "libclangmm/Index.h"
#include "libclangmm/Utility.h" #include "libclangmm/Utility.h"
#include "performance.h" #include "performance.h"
#include "position.h"
#include "serializer.h"
#include "utils.h"
#include <optional.h> #include <optional.h>
#include <rapidjson/writer.h> #include <rapidjson/document.h>
#include <rapidjson/prettywriter.h> #include <rapidjson/prettywriter.h>
#include <rapidjson/stringbuffer.h> #include <rapidjson/stringbuffer.h>
#include <rapidjson/document.h> #include <rapidjson/writer.h>
#include <algorithm> #include <algorithm>
#include <cstdint>
#include <cassert> #include <cassert>
#include <cstdint>
#include <fstream> #include <fstream>
#include <iostream> #include <iostream>
#include <unordered_map> #include <unordered_map>
#include <vector> #include <vector>
struct IndexType; struct IndexType;
struct IndexFunc; struct IndexFunc;
struct IndexVar; struct IndexVar;
@ -49,7 +52,7 @@ template <typename T>
struct hash<Id<T>> { struct hash<Id<T>> {
size_t operator()(const Id<T>& k) const { return hash<size_t>()(k.id); } size_t operator()(const Id<T>& k) const { return hash<size_t>()(k.id); }
}; };
} } // namespace std
template <typename T> template <typename T>
bool operator==(const Id<T>& a, const Id<T>& b) { bool operator==(const Id<T>& a, const Id<T>& b) {
@ -61,11 +64,11 @@ bool operator!=(const Id<T>& a, const Id<T>& b) {
return !(a == b); return !(a == b);
} }
template<typename T> template <typename T>
void Reflect(Reader& visitor, Id<T>& id) { void Reflect(Reader& visitor, Id<T>& id) {
id.id = visitor.GetUint64(); id.id = visitor.GetUint64();
} }
template<typename T> template <typename T>
void Reflect(Writer& visitor, Id<T>& value) { void Reflect(Writer& visitor, Id<T>& value) {
visitor.Uint64(value.id); visitor.Uint64(value.id);
} }
@ -76,7 +79,6 @@ using IndexVarId = Id<IndexVar>;
struct IdCache; struct IdCache;
struct IndexFuncRef { struct IndexFuncRef {
// NOTE: id can be -1 if the function call is not coming from a function. // NOTE: id can be -1 if the function call is not coming from a function.
IndexFuncId id; IndexFuncId id;
@ -85,19 +87,25 @@ struct IndexFuncRef {
IndexFuncRef() {} // For serialization. IndexFuncRef() {} // For serialization.
IndexFuncRef(IndexFuncId id, Range loc, bool is_implicit) : id(id), loc(loc), is_implicit(is_implicit) {} IndexFuncRef(IndexFuncId id, Range loc, bool is_implicit)
IndexFuncRef(Range loc, bool is_implicit) : id(IndexFuncId((size_t)-1)), loc(loc), is_implicit(is_implicit) {} : id(id), loc(loc), is_implicit(is_implicit) {}
IndexFuncRef(Range loc, bool is_implicit)
: id(IndexFuncId((size_t)-1)), loc(loc), is_implicit(is_implicit) {}
inline bool operator==(const IndexFuncRef& other) { inline bool operator==(const IndexFuncRef& other) {
return id == other.id && loc == other.loc && is_implicit == other.is_implicit; return id == other.id && loc == other.loc &&
is_implicit == other.is_implicit;
}
inline bool operator!=(const IndexFuncRef& other) {
return !(*this == other);
} }
inline bool operator!=(const IndexFuncRef& other) { return !(*this == other); }
inline bool operator<(const IndexFuncRef& other) const { inline bool operator<(const IndexFuncRef& other) const {
if (id < other.id) if (id < other.id)
return true; return true;
if (id == other.id && loc < other.loc) if (id == other.id && loc < other.loc)
return true; return true;
return id == other.id && loc == other.loc && is_implicit < other.is_implicit; return id == other.id && loc == other.loc &&
is_implicit < other.is_implicit;
} }
}; };
@ -125,10 +133,9 @@ inline void Reflect(Writer& visitor, IndexFuncRef& value) {
if (value.is_implicit) if (value.is_implicit)
s += "~"; s += "~";
if (value.id.id == -1) { // id.id is unsigned, special case 0 value if (value.id.id == -1) { // id.id is unsigned, special case 0 value
s += "-1"; s += "-1";
} } else {
else {
s += std::to_string(value.id.id); s += std::to_string(value.id.id);
} }
@ -136,10 +143,7 @@ inline void Reflect(Writer& visitor, IndexFuncRef& value) {
visitor.String(s.c_str()); visitor.String(s.c_str());
} }
template <typename TypeId, template <typename TypeId, typename FuncId, typename VarId, typename Range>
typename FuncId,
typename VarId,
typename Range>
struct TypeDefDefinitionData { struct TypeDefDefinitionData {
// General metadata. // General metadata.
std::string usr; std::string usr;
@ -173,19 +177,18 @@ struct TypeDefDefinitionData {
TypeDefDefinitionData() {} // For reflection. TypeDefDefinitionData() {} // For reflection.
TypeDefDefinitionData(const std::string& usr) : usr(usr) {} TypeDefDefinitionData(const std::string& usr) : usr(usr) {}
bool operator==(const TypeDefDefinitionData<TypeId, FuncId, VarId, Range>& bool operator==(
other) const { const TypeDefDefinitionData<TypeId, FuncId, VarId, Range>& other) const {
return usr == other.usr && short_name == other.short_name && return usr == other.usr && short_name == other.short_name &&
detailed_name == other.detailed_name && detailed_name == other.detailed_name &&
definition_spelling == other.definition_spelling && definition_spelling == other.definition_spelling &&
definition_extent == other.definition_extent && definition_extent == other.definition_extent &&
alias_of == other.alias_of && alias_of == other.alias_of && parents == other.parents &&
parents == other.parents && types == other.types && types == other.types && funcs == other.funcs && vars == other.vars;
funcs == other.funcs && vars == other.vars;
} }
bool operator!=(const TypeDefDefinitionData<TypeId, FuncId, VarId, Range>& bool operator!=(
other) const { const TypeDefDefinitionData<TypeId, FuncId, VarId, Range>& other) const {
return !(*this == other); return !(*this == other);
} }
}; };
@ -211,7 +214,8 @@ void Reflect(TVisitor& visitor,
} }
struct IndexType { struct IndexType {
using Def = TypeDefDefinitionData<IndexTypeId, IndexFuncId, IndexVarId, Range>; using Def =
TypeDefDefinitionData<IndexTypeId, IndexFuncId, IndexVarId, Range>;
Def def; Def def;
IndexTypeId id; IndexTypeId id;
@ -268,8 +272,8 @@ struct FuncDefDefinitionData {
} }
bool operator==( bool operator==(
const FuncDefDefinitionData<TypeId, FuncId, VarId, FuncRef, Range>& const FuncDefDefinitionData<TypeId, FuncId, VarId, FuncRef, Range>& other)
other) const { const {
return usr == other.usr && short_name == other.short_name && return usr == other.usr && short_name == other.short_name &&
detailed_name == other.detailed_name && detailed_name == other.detailed_name &&
definition_spelling == other.definition_spelling && definition_spelling == other.definition_spelling &&
@ -278,8 +282,8 @@ struct FuncDefDefinitionData {
locals == other.locals && callees == other.callees; locals == other.locals && callees == other.callees;
} }
bool operator!=( bool operator!=(
const FuncDefDefinitionData<TypeId, FuncId, VarId, FuncRef, Range>& const FuncDefDefinitionData<TypeId, FuncId, VarId, FuncRef, Range>& other)
other) const { const {
return !(*this == other); return !(*this == other);
} }
}; };
@ -307,7 +311,11 @@ void Reflect(
} }
struct IndexFunc { struct IndexFunc {
using Def = FuncDefDefinitionData<IndexTypeId, IndexFuncId, IndexVarId, IndexFuncRef, Range>; using Def = FuncDefDefinitionData<IndexTypeId,
IndexFuncId,
IndexVarId,
IndexFuncRef,
Range>;
Def def; Def def;
IndexFuncId id; IndexFuncId id;
@ -346,12 +354,13 @@ struct IndexFunc {
} }
}; };
MAKE_HASHABLE(IndexFunc, t.def.usr); MAKE_HASHABLE(IndexFunc, t.def.usr);
MAKE_REFLECT_STRUCT(IndexFunc::Declaration, spelling, extent, content, param_spellings); MAKE_REFLECT_STRUCT(IndexFunc::Declaration,
spelling,
extent,
content,
param_spellings);
template <typename TypeId, template <typename TypeId, typename FuncId, typename VarId, typename Range>
typename FuncId,
typename VarId,
typename Range>
struct VarDefDefinitionData { struct VarDefDefinitionData {
// General metadata. // General metadata.
std::string usr; std::string usr;
@ -377,8 +386,8 @@ struct VarDefDefinitionData {
VarDefDefinitionData() {} // For reflection. VarDefDefinitionData() {} // For reflection.
VarDefDefinitionData(const std::string& usr) : usr(usr) {} VarDefDefinitionData(const std::string& usr) : usr(usr) {}
bool operator==(const VarDefDefinitionData<TypeId, FuncId, VarId, Range>& bool operator==(
other) const { const VarDefDefinitionData<TypeId, FuncId, VarId, Range>& other) const {
return usr == other.usr && short_name == other.short_name && return usr == other.usr && short_name == other.short_name &&
detailed_name == other.detailed_name && detailed_name == other.detailed_name &&
declaration == other.declaration && declaration == other.declaration &&
@ -387,8 +396,8 @@ struct VarDefDefinitionData {
variable_type == other.variable_type && variable_type == other.variable_type &&
declaring_type == other.declaring_type; declaring_type == other.declaring_type;
} }
bool operator!=(const VarDefDefinitionData<TypeId, FuncId, VarId, Range>& bool operator!=(
other) const { const VarDefDefinitionData<TypeId, FuncId, VarId, Range>& other) const {
return !(*this == other); return !(*this == other);
} }
}; };
@ -509,9 +518,11 @@ struct FileContents {
// |import_file| is the cc file which is what gets passed to clang. // |import_file| is the cc file which is what gets passed to clang.
// |desired_index_file| is the (h or cc) file which has actually changed. // |desired_index_file| is the (h or cc) file which has actually changed.
// |dependencies| are the existing dependencies of |import_file| if this is a reparse. // |dependencies| are the existing dependencies of |import_file| if this is a
// reparse.
std::vector<std::unique_ptr<IndexFile>> Parse( std::vector<std::unique_ptr<IndexFile>> Parse(
Config* config, FileConsumer::SharedState* file_consumer_shared, Config* config,
FileConsumer::SharedState* file_consumer_shared,
std::string file, std::string file,
std::vector<std::string> args, std::vector<std::string> args,
std::vector<FileContents> file_contents, std::vector<FileContents> file_contents,

View File

@ -4,88 +4,87 @@
const char* IpcIdToString(IpcId id) { const char* IpcIdToString(IpcId id) {
switch (id) { switch (id) {
case IpcId::CancelRequest: case IpcId::CancelRequest:
return "$/cancelRequest"; return "$/cancelRequest";
case IpcId::Initialize: case IpcId::Initialize:
return "initialize"; return "initialize";
case IpcId::Initialized: case IpcId::Initialized:
return "initialized"; return "initialized";
case IpcId::Exit: case IpcId::Exit:
return "exit"; return "exit";
case IpcId::TextDocumentDidOpen: case IpcId::TextDocumentDidOpen:
return "textDocument/didOpen"; return "textDocument/didOpen";
case IpcId::TextDocumentDidChange: case IpcId::TextDocumentDidChange:
return "textDocument/didChange"; return "textDocument/didChange";
case IpcId::TextDocumentDidClose: case IpcId::TextDocumentDidClose:
return "textDocument/didClose"; return "textDocument/didClose";
case IpcId::TextDocumentDidSave: case IpcId::TextDocumentDidSave:
return "textDocument/didSave"; return "textDocument/didSave";
case IpcId::TextDocumentPublishDiagnostics: case IpcId::TextDocumentPublishDiagnostics:
return "textDocument/publishDiagnostics"; return "textDocument/publishDiagnostics";
case IpcId::TextDocumentRename: case IpcId::TextDocumentRename:
return "textDocument/rename"; return "textDocument/rename";
case IpcId::TextDocumentCompletion: case IpcId::TextDocumentCompletion:
return "textDocument/completion"; return "textDocument/completion";
case IpcId::TextDocumentSignatureHelp: case IpcId::TextDocumentSignatureHelp:
return "textDocument/signatureHelp"; return "textDocument/signatureHelp";
case IpcId::TextDocumentDefinition: case IpcId::TextDocumentDefinition:
return "textDocument/definition"; return "textDocument/definition";
case IpcId::TextDocumentDocumentHighlight: case IpcId::TextDocumentDocumentHighlight:
return "textDocument/documentHighlight"; return "textDocument/documentHighlight";
case IpcId::TextDocumentHover: case IpcId::TextDocumentHover:
return "textDocument/hover"; return "textDocument/hover";
case IpcId::TextDocumentReferences: case IpcId::TextDocumentReferences:
return "textDocument/references"; return "textDocument/references";
case IpcId::TextDocumentDocumentSymbol: case IpcId::TextDocumentDocumentSymbol:
return "textDocument/documentSymbol"; return "textDocument/documentSymbol";
case IpcId::TextDocumentDocumentLink: case IpcId::TextDocumentDocumentLink:
return "textDocument/documentLink"; return "textDocument/documentLink";
case IpcId::TextDocumentCodeAction: case IpcId::TextDocumentCodeAction:
return "textDocument/codeAction"; return "textDocument/codeAction";
case IpcId::TextDocumentCodeLens: case IpcId::TextDocumentCodeLens:
return "textDocument/codeLens"; return "textDocument/codeLens";
case IpcId::CodeLensResolve: case IpcId::CodeLensResolve:
return "codeLens/resolve"; return "codeLens/resolve";
case IpcId::WorkspaceSymbol: case IpcId::WorkspaceSymbol:
return "workspace/symbol"; return "workspace/symbol";
case IpcId::CqueryPublishInactiveRegions: case IpcId::CqueryPublishInactiveRegions:
return "$cquery/publishInactiveRegions"; return "$cquery/publishInactiveRegions";
case IpcId::CqueryFreshenIndex: case IpcId::CqueryFreshenIndex:
return "$cquery/freshenIndex"; return "$cquery/freshenIndex";
case IpcId::CqueryTypeHierarchyTree: case IpcId::CqueryTypeHierarchyTree:
return "$cquery/typeHierarchyTree"; return "$cquery/typeHierarchyTree";
case IpcId::CqueryCallTreeInitial: case IpcId::CqueryCallTreeInitial:
return "$cquery/callTreeInitial"; return "$cquery/callTreeInitial";
case IpcId::CqueryCallTreeExpand: case IpcId::CqueryCallTreeExpand:
return "$cquery/callTreeExpand"; return "$cquery/callTreeExpand";
case IpcId::CqueryVars: case IpcId::CqueryVars:
return "$cquery/vars"; return "$cquery/vars";
case IpcId::CqueryCallers: case IpcId::CqueryCallers:
return "$cquery/callers"; return "$cquery/callers";
case IpcId::CqueryBase: case IpcId::CqueryBase:
return "$cquery/base"; return "$cquery/base";
case IpcId::CqueryDerived: case IpcId::CqueryDerived:
return "$cquery/derived"; return "$cquery/derived";
case IpcId::Cout: case IpcId::Cout:
return "$cout"; return "$cout";
case IpcId::CqueryIndexFile: case IpcId::CqueryIndexFile:
return "$cquery/indexFile"; return "$cquery/indexFile";
case IpcId::CqueryQueryDbWaitForIdleIndexer: case IpcId::CqueryQueryDbWaitForIdleIndexer:
return "$cquery/queryDbWaitForIdleIndexer"; return "$cquery/queryDbWaitForIdleIndexer";
case IpcId::CqueryExitWhenIdle: case IpcId::CqueryExitWhenIdle:
return "$cquery/exitWhenIdle"; return "$cquery/exitWhenIdle";
default: default:
assert(false && "missing IpcId string name"); assert(false && "missing IpcId string name");
exit(1); exit(1);
} }
} }
BaseIpcMessage::BaseIpcMessage(IpcId method_id) BaseIpcMessage::BaseIpcMessage(IpcId method_id) : method_id(method_id) {}
: method_id(method_id) {}
BaseIpcMessage::~BaseIpcMessage() = default; BaseIpcMessage::~BaseIpcMessage() = default;

View File

@ -1,7 +1,8 @@
#pragma once #pragma once
#include "utils.h"
#include "serializer.h" #include "serializer.h"
#include "utils.h"
#include <string> #include <string>
@ -40,10 +41,10 @@ enum class IpcId : int {
CqueryCallTreeInitial, CqueryCallTreeInitial,
CqueryCallTreeExpand, CqueryCallTreeExpand,
// These are like DocumentReferences but show different types of data. // These are like DocumentReferences but show different types of data.
CqueryVars, // Show all variables of a type. CqueryVars, // Show all variables of a type.
CqueryCallers, // Show all callers of a function. CqueryCallers, // Show all callers of a function.
CqueryBase, // Show base types/method. CqueryBase, // Show base types/method.
CqueryDerived, // Show all derived types/methods. CqueryDerived, // Show all derived types/methods.
// Internal implementation detail. // Internal implementation detail.
Cout, Cout,
@ -88,10 +89,15 @@ struct Ipc_CqueryIndexFile : public IpcMessage<Ipc_CqueryIndexFile> {
}; };
Params params; Params params;
}; };
MAKE_REFLECT_STRUCT(Ipc_CqueryIndexFile::Params, path, args, is_interactive, contents); MAKE_REFLECT_STRUCT(Ipc_CqueryIndexFile::Params,
path,
args,
is_interactive,
contents);
MAKE_REFLECT_STRUCT(Ipc_CqueryIndexFile, params); MAKE_REFLECT_STRUCT(Ipc_CqueryIndexFile, params);
struct Ipc_CqueryQueryDbWaitForIdleIndexer : public IpcMessage<Ipc_CqueryQueryDbWaitForIdleIndexer> { struct Ipc_CqueryQueryDbWaitForIdleIndexer
: public IpcMessage<Ipc_CqueryQueryDbWaitForIdleIndexer> {
static constexpr IpcId kIpcId = IpcId::CqueryQueryDbWaitForIdleIndexer; static constexpr IpcId kIpcId = IpcId::CqueryQueryDbWaitForIdleIndexer;
}; };
MAKE_REFLECT_EMPTY_STRUCT(Ipc_CqueryQueryDbWaitForIdleIndexer); MAKE_REFLECT_EMPTY_STRUCT(Ipc_CqueryQueryDbWaitForIdleIndexer);

View File

@ -2,7 +2,7 @@
IpcManager* IpcManager::instance_ = nullptr; IpcManager* IpcManager::instance_ = nullptr;
// static // static
IpcManager* IpcManager::instance() { IpcManager* IpcManager::instance() {
return instance_; return instance_;
} }
@ -12,8 +12,10 @@ void IpcManager::CreateInstance(MultiQueueWaiter* waiter) {
instance_ = new IpcManager(waiter); instance_ = new IpcManager(waiter);
} }
ThreadedQueue<std::unique_ptr<BaseIpcMessage>>* IpcManager::GetThreadedQueue(Destination destination) { ThreadedQueue<std::unique_ptr<BaseIpcMessage>>* IpcManager::GetThreadedQueue(
return destination == Destination::Client ? threaded_queue_for_client_.get() : threaded_queue_for_server_.get(); Destination destination) {
return destination == Destination::Client ? threaded_queue_for_client_.get()
: threaded_queue_for_server_.get();
} }
void IpcManager::SendOutMessageToClient(IpcId id, lsBaseOutMessage& response) { void IpcManager::SendOutMessageToClient(IpcId id, lsBaseOutMessage& response) {
@ -26,15 +28,19 @@ void IpcManager::SendOutMessageToClient(IpcId id, lsBaseOutMessage& response) {
GetThreadedQueue(Destination::Client)->Enqueue(std::move(out)); GetThreadedQueue(Destination::Client)->Enqueue(std::move(out));
} }
void IpcManager::SendMessage(Destination destination, std::unique_ptr<BaseIpcMessage> message) { void IpcManager::SendMessage(Destination destination,
std::unique_ptr<BaseIpcMessage> message) {
GetThreadedQueue(destination)->Enqueue(std::move(message)); GetThreadedQueue(destination)->Enqueue(std::move(message));
} }
std::vector<std::unique_ptr<BaseIpcMessage>> IpcManager::GetMessages(Destination destination) { std::vector<std::unique_ptr<BaseIpcMessage>> IpcManager::GetMessages(
Destination destination) {
return GetThreadedQueue(destination)->DequeueAll(); return GetThreadedQueue(destination)->DequeueAll();
} }
IpcManager::IpcManager(MultiQueueWaiter* waiter) { IpcManager::IpcManager(MultiQueueWaiter* waiter) {
threaded_queue_for_client_ = MakeUnique<ThreadedQueue<std::unique_ptr<BaseIpcMessage>>>(waiter); threaded_queue_for_client_ =
threaded_queue_for_server_ = MakeUnique<ThreadedQueue<std::unique_ptr<BaseIpcMessage>>>(waiter); MakeUnique<ThreadedQueue<std::unique_ptr<BaseIpcMessage>>>(waiter);
threaded_queue_for_server_ =
MakeUnique<ThreadedQueue<std::unique_ptr<BaseIpcMessage>>>(waiter);
} }

View File

@ -11,21 +11,24 @@ struct IpcManager {
static IpcManager* instance(); static IpcManager* instance();
static void CreateInstance(MultiQueueWaiter* waiter); static void CreateInstance(MultiQueueWaiter* waiter);
std::unique_ptr<ThreadedQueue<std::unique_ptr<BaseIpcMessage>>> threaded_queue_for_client_; std::unique_ptr<ThreadedQueue<std::unique_ptr<BaseIpcMessage>>>
std::unique_ptr<ThreadedQueue<std::unique_ptr<BaseIpcMessage>>> threaded_queue_for_server_; threaded_queue_for_client_;
std::unique_ptr<ThreadedQueue<std::unique_ptr<BaseIpcMessage>>>
threaded_queue_for_server_;
enum class Destination { enum class Destination { Client, Server };
Client, Server
};
ThreadedQueue<std::unique_ptr<BaseIpcMessage>>* GetThreadedQueue(Destination destination); ThreadedQueue<std::unique_ptr<BaseIpcMessage>>* GetThreadedQueue(
Destination destination);
void SendOutMessageToClient(IpcId id, lsBaseOutMessage& response); void SendOutMessageToClient(IpcId id, lsBaseOutMessage& response);
void SendMessage(Destination destination, std::unique_ptr<BaseIpcMessage> message); void SendMessage(Destination destination,
std::unique_ptr<BaseIpcMessage> message);
std::vector<std::unique_ptr<BaseIpcMessage>> GetMessages(Destination destination); std::vector<std::unique_ptr<BaseIpcMessage>> GetMessages(
Destination destination);
private: private:
IpcManager(MultiQueueWaiter* waiter); IpcManager(MultiQueueWaiter* waiter);
}; };

View File

@ -8,8 +8,7 @@ void Reflect(Writer& visitor, lsRequestId& value) {
if (value.id0) { if (value.id0) {
Reflect(visitor, value.id0.value()); Reflect(visitor, value.id0.value());
} } else {
else {
Reflect(visitor, value.id1.value()); Reflect(visitor, value.id1.value());
} }
} }
@ -26,7 +25,8 @@ void Reflect(Reader& visitor, lsRequestId& id) {
MessageRegistry* MessageRegistry::instance_ = nullptr; MessageRegistry* MessageRegistry::instance_ = nullptr;
// Reads a JsonRpc message. |read| returns the next input character. // Reads a JsonRpc message. |read| returns the next input character.
optional<std::string> ReadJsonRpcContentFrom(std::function<optional<char>()> read) { optional<std::string> ReadJsonRpcContentFrom(
std::function<optional<char>()> read) {
// Read the content length. It is terminated by the "\r\n" sequence. // Read the content length. It is terminated by the "\r\n" sequence.
int exit_seq = 0; int exit_seq = 0;
std::string stringified_content_length; std::string stringified_content_length;
@ -38,14 +38,17 @@ optional<std::string> ReadJsonRpcContentFrom(std::function<optional<char>()> rea
} }
char c = *opt_c; char c = *opt_c;
if (exit_seq == 0 && c == '\r') ++exit_seq; if (exit_seq == 0 && c == '\r')
if (exit_seq == 1 && c == '\n') break; ++exit_seq;
if (exit_seq == 1 && c == '\n')
break;
stringified_content_length += c; stringified_content_length += c;
} }
const char* kContentLengthStart = "Content-Length: "; const char* kContentLengthStart = "Content-Length: ";
assert(StartsWith(stringified_content_length, kContentLengthStart)); assert(StartsWith(stringified_content_length, kContentLengthStart));
int content_length = atoi(stringified_content_length.c_str() + strlen(kContentLengthStart)); int content_length =
atoi(stringified_content_length.c_str() + strlen(kContentLengthStart));
// There is always a "\r\n" sequence before the actual content. // There is always a "\r\n" sequence before the actual content.
auto expect_char = [&](char expected) { auto expect_char = [&](char expected) {
@ -74,8 +77,8 @@ optional<std::string> ReadJsonRpcContentFrom(std::function<optional<char>()> rea
TEST_SUITE("FindIncludeLine"); TEST_SUITE("FindIncludeLine");
std::function<optional<char>()> std::function<optional<char>()> MakeContentReader(std::string* content,
MakeContentReader(std::string* content, bool can_be_empty) { bool can_be_empty) {
return [content, can_be_empty]() -> optional<char> { return [content, can_be_empty]() -> optional<char> {
if (!can_be_empty) if (!can_be_empty)
REQUIRE(!content->empty()); REQUIRE(!content->empty());
@ -106,7 +109,8 @@ TEST_CASE("ReadContentFromSource") {
REQUIRE(parse_incorrect("ggg") == optional<std::string>()); REQUIRE(parse_incorrect("ggg") == optional<std::string>());
REQUIRE(parse_incorrect("Content-Length: 0\r\n") == optional<std::string>()); REQUIRE(parse_incorrect("Content-Length: 0\r\n") == optional<std::string>());
REQUIRE(parse_incorrect("Content-Length: 5\r\n\r\nab") == optional<std::string>()); REQUIRE(parse_incorrect("Content-Length: 5\r\n\r\nab") ==
optional<std::string>());
} }
TEST_SUITE_END(); TEST_SUITE_END();
@ -125,7 +129,8 @@ optional<char> ReadCharFromStdinBlocking() {
} }
std::unique_ptr<BaseIpcMessage> MessageRegistry::ReadMessageFromStdin() { std::unique_ptr<BaseIpcMessage> MessageRegistry::ReadMessageFromStdin() {
optional<std::string> content = ReadJsonRpcContentFrom(&ReadCharFromStdinBlocking); optional<std::string> content =
ReadJsonRpcContentFrom(&ReadCharFromStdinBlocking);
if (!content) { if (!content) {
LOG_S(FATAL) << "Failed to read JsonRpc input; exiting"; LOG_S(FATAL) << "Failed to read JsonRpc input; exiting";
exit(1); exit(1);
@ -139,7 +144,8 @@ std::unique_ptr<BaseIpcMessage> MessageRegistry::ReadMessageFromStdin() {
} }
std::unique_ptr<BaseIpcMessage> MessageRegistry::Parse(Reader& visitor) { std::unique_ptr<BaseIpcMessage> MessageRegistry::Parse(Reader& visitor) {
if (!visitor.HasMember("jsonrpc") || std::string(visitor["jsonrpc"].GetString()) != "2.0") { if (!visitor.HasMember("jsonrpc") ||
std::string(visitor["jsonrpc"].GetString()) != "2.0") {
std::cerr << "Bad or missing jsonrpc version" << std::endl; std::cerr << "Bad or missing jsonrpc version" << std::endl;
exit(1); exit(1);
} }
@ -148,7 +154,8 @@ std::unique_ptr<BaseIpcMessage> MessageRegistry::Parse(Reader& visitor) {
ReflectMember(visitor, "method", method); ReflectMember(visitor, "method", method);
if (allocators.find(method) == allocators.end()) { if (allocators.find(method) == allocators.end()) {
LOG_S(ERROR) << "Unable to find registered handler for method \"" << method << "\"" << std::endl; LOG_S(ERROR) << "Unable to find registered handler for method \"" << method
<< "\"" << std::endl;
return nullptr; return nullptr;
} }
@ -196,8 +203,9 @@ void lsDocumentUri::SetPath(const std::string& path) {
raw_uri = path; raw_uri = path;
size_t index = raw_uri.find(":"); size_t index = raw_uri.find(":");
if (index == 1) { // widows drive letters must always be 1 char if (index == 1) { // widows drive letters must always be 1 char
raw_uri.replace(raw_uri.begin() + index, raw_uri.begin() + index + 1, "%3A"); raw_uri.replace(raw_uri.begin() + index, raw_uri.begin() + index + 1,
"%3A");
} }
raw_uri = ReplaceAll(raw_uri, " ", "%20"); raw_uri = ReplaceAll(raw_uri, " ", "%20");
@ -210,7 +218,7 @@ void lsDocumentUri::SetPath(const std::string& path) {
#else #else
raw_uri = "file://" + raw_uri; raw_uri = "file://" + raw_uri;
#endif #endif
//std::cerr << "Set uri to " << raw_uri << " from " << path; // std::cerr << "Set uri to " << raw_uri << " from " << path;
} }
std::string lsDocumentUri::GetPath() const { std::string lsDocumentUri::GetPath() const {
@ -242,14 +250,15 @@ std::string lsDocumentUri::GetPath() const {
std::replace(result.begin(), result.end(), '\\', '/'); std::replace(result.begin(), result.end(), '\\', '/');
#if defined(_WIN32) #if defined(_WIN32)
//std::transform(result.begin(), result.end(), result.begin(), ::tolower); // std::transform(result.begin(), result.end(), result.begin(), ::tolower);
#endif #endif
return result; return result;
} }
lsPosition::lsPosition() {} lsPosition::lsPosition() {}
lsPosition::lsPosition(int line, int character) : line(line), character(character) {} lsPosition::lsPosition(int line, int character)
: line(line), character(character) {}
bool lsPosition::operator==(const lsPosition& other) const { bool lsPosition::operator==(const lsPosition& other) const {
return line == other.line && character == other.character; return line == other.line && character == other.character;
@ -267,7 +276,8 @@ bool lsRange::operator==(const lsRange& other) const {
} }
lsLocation::lsLocation() {} lsLocation::lsLocation() {}
lsLocation::lsLocation(lsDocumentUri uri, lsRange range) : uri(uri), range(range) {} lsLocation::lsLocation(lsDocumentUri uri, lsRange range)
: uri(uri), range(range) {}
bool lsLocation::operator==(const lsLocation& other) const { bool lsLocation::operator==(const lsLocation& other) const {
return uri == other.uri && range == other.range; return uri == other.uri && range == other.range;
@ -297,15 +307,15 @@ void Reflect(Reader& reader, lsInitializeParams::lsTrace& value) {
void Reflect(Writer& writer, lsInitializeParams::lsTrace& value) { void Reflect(Writer& writer, lsInitializeParams::lsTrace& value) {
switch (value) { switch (value) {
case lsInitializeParams::lsTrace::Off: case lsInitializeParams::lsTrace::Off:
writer.String("off"); writer.String("off");
break; break;
case lsInitializeParams::lsTrace::Messages: case lsInitializeParams::lsTrace::Messages:
writer.String("messages"); writer.String("messages");
break; break;
case lsInitializeParams::lsTrace::Verbose: case lsInitializeParams::lsTrace::Verbose:
writer.String("verbose"); writer.String("verbose");
break; break;
} }
} }

View File

@ -14,8 +14,8 @@
#include <unordered_map> #include <unordered_map>
#include <unordered_set> #include <unordered_set>
using std::experimental::optional;
using std::experimental::nullopt; using std::experimental::nullopt;
using std::experimental::optional;
///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////
@ -79,10 +79,11 @@ struct MessageRegistry {
static MessageRegistry* instance_; static MessageRegistry* instance_;
static MessageRegistry* instance(); static MessageRegistry* instance();
using Allocator = std::function<std::unique_ptr<BaseIpcMessage>(Reader& visitor)>; using Allocator =
std::function<std::unique_ptr<BaseIpcMessage>(Reader& visitor)>;
std::unordered_map<std::string, Allocator> allocators; std::unordered_map<std::string, Allocator> allocators;
template<typename T> template <typename T>
void Register() { void Register() {
std::string method_name = IpcIdToString(T::kIpcId); std::string method_name = IpcIdToString(T::kIpcId);
allocators[method_name] = [](Reader& visitor) { allocators[method_name] = [](Reader& visitor) {
@ -96,13 +97,12 @@ struct MessageRegistry {
std::unique_ptr<BaseIpcMessage> Parse(Reader& visitor); std::unique_ptr<BaseIpcMessage> Parse(Reader& visitor);
}; };
struct lsBaseOutMessage { struct lsBaseOutMessage {
virtual ~lsBaseOutMessage(); virtual ~lsBaseOutMessage();
virtual void Write(std::ostream& out) = 0; virtual void Write(std::ostream& out) = 0;
}; };
template<typename TDerived> template <typename TDerived>
struct lsOutMessage : lsBaseOutMessage { struct lsOutMessage : lsBaseOutMessage {
// All derived types need to reflect on the |jsonrpc| member. // All derived types need to reflect on the |jsonrpc| member.
std::string jsonrpc = "2.0"; std::string jsonrpc = "2.0";
@ -115,7 +115,7 @@ struct lsOutMessage : lsBaseOutMessage {
Reflect(writer, *that); Reflect(writer, *that);
out << "Content-Length: " << output.GetSize(); out << "Content-Length: " << output.GetSize();
out << (char)13 << char(10) << char(13) << char(10); // CRLFCRLF out << (char)13 << char(10) << char(13) << char(10); // CRLFCRLF
out << output.GetString(); out << output.GetString();
out.flush(); out.flush();
} }
@ -216,12 +216,11 @@ struct lsDocumentUri {
}; };
MAKE_HASHABLE(lsDocumentUri, t.raw_uri); MAKE_HASHABLE(lsDocumentUri, t.raw_uri);
template<typename TVisitor> template <typename TVisitor>
void Reflect(TVisitor& visitor, lsDocumentUri& value) { void Reflect(TVisitor& visitor, lsDocumentUri& value) {
Reflect(visitor, value.raw_uri); Reflect(visitor, value.raw_uri);
} }
struct lsPosition { struct lsPosition {
lsPosition(); lsPosition();
lsPosition(int line, int character); lsPosition(int line, int character);
@ -291,7 +290,7 @@ struct lsSymbolInformation {
}; };
MAKE_REFLECT_STRUCT(lsSymbolInformation, name, kind, location, containerName); MAKE_REFLECT_STRUCT(lsSymbolInformation, name, kind, location, containerName);
template<typename T> template <typename T>
struct lsCommand { struct lsCommand {
// Title of the command (ie, 'save') // Title of the command (ie, 'save')
std::string title; std::string title;
@ -302,7 +301,7 @@ struct lsCommand {
// MAKE_REFLECT_STRUCT_WRITER_AS_ARRAY. // MAKE_REFLECT_STRUCT_WRITER_AS_ARRAY.
T arguments; T arguments;
}; };
template<typename TVisitor, typename T> template <typename TVisitor, typename T>
void Reflect(TVisitor& visitor, lsCommand<T>& value) { void Reflect(TVisitor& visitor, lsCommand<T>& value) {
REFLECT_MEMBER_START(); REFLECT_MEMBER_START();
REFLECT_MEMBER(title); REFLECT_MEMBER(title);
@ -311,7 +310,7 @@ void Reflect(TVisitor& visitor, lsCommand<T>& value) {
REFLECT_MEMBER_END(); REFLECT_MEMBER_END();
} }
template<typename TData, typename TCommandArguments> template <typename TData, typename TCommandArguments>
struct lsCodeLens { struct lsCodeLens {
// The range in which this code lens is valid. Should only span a single line. // The range in which this code lens is valid. Should only span a single line.
lsRange range; lsRange range;
@ -321,7 +320,7 @@ struct lsCodeLens {
// a code lens and a code lens resolve request. // a code lens and a code lens resolve request.
TData data; TData data;
}; };
template<typename TVisitor, typename TData, typename TCommandArguments> template <typename TVisitor, typename TData, typename TCommandArguments>
void Reflect(TVisitor& visitor, lsCodeLens<TData, TCommandArguments>& value) { void Reflect(TVisitor& visitor, lsCodeLens<TData, TCommandArguments>& value) {
REFLECT_MEMBER_START(); REFLECT_MEMBER_START();
REFLECT_MEMBER(range); REFLECT_MEMBER(range);
@ -377,7 +376,8 @@ enum class lsInsertTextFormat {
// the end of the snippet. Placeholders with equal identifiers are linked, // the end of the snippet. Placeholders with equal identifiers are linked,
// that is typing in one will update others too. // that is typing in one will update others too.
// //
// See also: https://github.com/Microsoft/vscode/blob/master/src/vs/editor/contrib/snippet/common/snippet.md // See also:
// https://github.com/Microsoft/vscode/blob/master/src/vs/editor/contrib/snippet/common/snippet.md
Snippet = 2 Snippet = 2
}; };
MAKE_REFLECT_TYPE_PROXY(lsInsertTextFormat, int); MAKE_REFLECT_TYPE_PROXY(lsInsertTextFormat, int);
@ -406,7 +406,8 @@ enum class lsCompletionItemKind {
MAKE_REFLECT_TYPE_PROXY(lsCompletionItemKind, int); MAKE_REFLECT_TYPE_PROXY(lsCompletionItemKind, int);
struct lsCompletionItem { struct lsCompletionItem {
// A set of function parameters. Used internally for signature help. Not sent to vscode. // A set of function parameters. Used internally for signature help. Not sent
// to vscode.
std::vector<std::string> parameters_; std::vector<std::string> parameters_;
// The label of this completion item. By default // The label of this completion item. By default
@ -431,21 +432,21 @@ struct lsCompletionItem {
// A string that should be used when filtering a set of // A string that should be used when filtering a set of
// completion items. When `falsy` the label is used. // completion items. When `falsy` the label is used.
//std::string filterText; // std::string filterText;
// A string that should be inserted a document when selecting // A string that should be inserted a document when selecting
// this completion. When `falsy` the label is used. // this completion. When `falsy` the label is used.
std::string insertText; std::string insertText;
// The format of the insert text. The format applies to both the `insertText` property // The format of the insert text. The format applies to both the `insertText`
// and the `newText` property of a provided `textEdit`. // property and the `newText` property of a provided `textEdit`.
lsInsertTextFormat insertTextFormat = lsInsertTextFormat::Snippet; lsInsertTextFormat insertTextFormat = lsInsertTextFormat::Snippet;
// An edit which is applied to a document when selecting this completion. When an edit is provided the value of // An edit which is applied to a document when selecting this completion. When
// `insertText` is ignored. // an edit is provided the value of `insertText` is ignored.
// //
// *Note:* The range of the edit must be a single line range and it must contain the position at which completion // *Note:* The range of the edit must be a single line range and it must
// has been requested. // contain the position at which completion has been requested.
optional<lsTextEdit> textEdit; optional<lsTextEdit> textEdit;
// An optional array of additional text edits that are applied when // An optional array of additional text edits that are applied when
@ -453,10 +454,9 @@ struct lsCompletionItem {
// nor with themselves. // nor with themselves.
// std::vector<TextEdit> additionalTextEdits; // std::vector<TextEdit> additionalTextEdits;
// An optional command that is executed *after* inserting this completion. *Note* that // An optional command that is executed *after* inserting this completion.
// additional modifications to the current document should be described with the // *Note* that additional modifications to the current document should be
// additionalTextEdits-property. // described with the additionalTextEdits-property. Command command;
// Command command;
// An data entry field that is preserved on a completion item between // An data entry field that is preserved on a completion item between
// a completion and a completion resolve request. // a completion and a completion resolve request.
@ -468,14 +468,14 @@ struct lsCompletionItem {
const std::string& InsertedContent() const; const std::string& InsertedContent() const;
}; };
MAKE_REFLECT_STRUCT(lsCompletionItem, MAKE_REFLECT_STRUCT(lsCompletionItem,
label, label,
kind, kind,
detail, detail,
documentation, documentation,
sortText, sortText,
insertText, insertText,
insertTextFormat, insertTextFormat,
textEdit); textEdit);
struct lsTextDocumentItem { struct lsTextDocumentItem {
// The text document's URI. // The text document's URI.
@ -505,7 +505,7 @@ MAKE_REFLECT_STRUCT(lsTextDocumentEdit, textDocument, edits);
struct lsWorkspaceEdit { struct lsWorkspaceEdit {
// Holds changes to existing resources. // Holds changes to existing resources.
// changes ? : { [uri:string]: TextEdit[]; }; // changes ? : { [uri:string]: TextEdit[]; };
//std::unordered_map<lsDocumentUri, std::vector<lsTextEdit>> changes; // std::unordered_map<lsDocumentUri, std::vector<lsTextEdit>> changes;
// An array of `TextDocumentEdit`s to express changes to specific a specific // An array of `TextDocumentEdit`s to express changes to specific a specific
// version of a text document. Whether a client supports versioned document // version of a text document. Whether a client supports versioned document
@ -529,10 +529,10 @@ MAKE_REFLECT_TYPE_PROXY(lsDocumentHighlightKind, int);
// special attention. Usually a document highlight is visualized by changing // special attention. Usually a document highlight is visualized by changing
// the background color of its range. // the background color of its range.
struct lsDocumentHighlight { struct lsDocumentHighlight {
// The range this highlight applies to. // The range this highlight applies to.
lsRange range; lsRange range;
// The highlight kind, default is DocumentHighlightKind.Text. // The highlight kind, default is DocumentHighlightKind.Text.
lsDocumentHighlightKind kind = lsDocumentHighlightKind::Text; lsDocumentHighlightKind kind = lsDocumentHighlightKind::Text;
}; };
MAKE_REFLECT_STRUCT(lsDocumentHighlight, range, kind); MAKE_REFLECT_STRUCT(lsDocumentHighlight, range, kind);
@ -572,7 +572,6 @@ struct lsDiagnostic {
}; };
MAKE_REFLECT_STRUCT(lsDiagnostic, range, severity, source, message); MAKE_REFLECT_STRUCT(lsDiagnostic, range, severity, source, message);
// TODO: DocumentFilter // TODO: DocumentFilter
// TODO: DocumentSelector // TODO: DocumentSelector
@ -687,17 +686,17 @@ struct lsWorkspaceClientCapabilites {
// Capabilities specific to `WorkspaceEdit`s // Capabilities specific to `WorkspaceEdit`s
optional<lsWorkspaceEdit> workspaceEdit; optional<lsWorkspaceEdit> workspaceEdit;
struct lsGenericDynamicReg { struct lsGenericDynamicReg {
// Did foo notification supports dynamic registration. // Did foo notification supports dynamic registration.
optional<bool> dynamicRegistration; optional<bool> dynamicRegistration;
}; };
// Capabilities specific to the `workspace/didChangeConfiguration`
// Capabilities specific to the `workspace/didChangeConfiguration` notification. // notification.
optional<lsGenericDynamicReg> didChangeConfiguration; optional<lsGenericDynamicReg> didChangeConfiguration;
// Capabilities specific to the `workspace/didChangeWatchedFiles` notification. // Capabilities specific to the `workspace/didChangeWatchedFiles`
// notification.
optional<lsGenericDynamicReg> didChangeWatchedFiles; optional<lsGenericDynamicReg> didChangeWatchedFiles;
// Capabilities specific to the `workspace/symbol` request. // Capabilities specific to the `workspace/symbol` request.
@ -707,17 +706,17 @@ struct lsWorkspaceClientCapabilites {
optional<lsGenericDynamicReg> executeCommand; optional<lsGenericDynamicReg> executeCommand;
}; };
MAKE_REFLECT_STRUCT(lsWorkspaceClientCapabilites::lsWorkspaceEdit, documentChanges); MAKE_REFLECT_STRUCT(lsWorkspaceClientCapabilites::lsWorkspaceEdit,
MAKE_REFLECT_STRUCT(lsWorkspaceClientCapabilites::lsGenericDynamicReg, dynamicRegistration); documentChanges);
MAKE_REFLECT_STRUCT(lsWorkspaceClientCapabilites::lsGenericDynamicReg,
dynamicRegistration);
MAKE_REFLECT_STRUCT(lsWorkspaceClientCapabilites, MAKE_REFLECT_STRUCT(lsWorkspaceClientCapabilites,
applyEdit, applyEdit,
workspaceEdit, workspaceEdit,
didChangeConfiguration, didChangeConfiguration,
didChangeWatchedFiles, didChangeWatchedFiles,
symbol, symbol,
executeCommand); executeCommand);
// Text document specific client capabilities. // Text document specific client capabilities.
struct lsTextDocumentClientCapabilities { struct lsTextDocumentClientCapabilities {
@ -810,27 +809,39 @@ struct lsTextDocumentClientCapabilities {
optional<lsGenericDynamicReg> rename; optional<lsGenericDynamicReg> rename;
}; };
MAKE_REFLECT_STRUCT(lsTextDocumentClientCapabilities::lsSynchronization, dynamicRegistration, willSave, willSaveWaitUntil, didSave); MAKE_REFLECT_STRUCT(lsTextDocumentClientCapabilities::lsSynchronization,
MAKE_REFLECT_STRUCT(lsTextDocumentClientCapabilities::lsCompletion, dynamicRegistration, completionItem); dynamicRegistration,
MAKE_REFLECT_STRUCT(lsTextDocumentClientCapabilities::lsCompletion::lsCompletionItem, snippetSupport); willSave,
MAKE_REFLECT_STRUCT(lsTextDocumentClientCapabilities::lsGenericDynamicReg, dynamicRegistration); willSaveWaitUntil,
MAKE_REFLECT_STRUCT(lsTextDocumentClientCapabilities::CodeLensRegistrationOptions, dynamicRegistration, resolveProvider); didSave);
MAKE_REFLECT_STRUCT(lsTextDocumentClientCapabilities::lsCompletion,
dynamicRegistration,
completionItem);
MAKE_REFLECT_STRUCT(
lsTextDocumentClientCapabilities::lsCompletion::lsCompletionItem,
snippetSupport);
MAKE_REFLECT_STRUCT(lsTextDocumentClientCapabilities::lsGenericDynamicReg,
dynamicRegistration);
MAKE_REFLECT_STRUCT(
lsTextDocumentClientCapabilities::CodeLensRegistrationOptions,
dynamicRegistration,
resolveProvider);
MAKE_REFLECT_STRUCT(lsTextDocumentClientCapabilities, MAKE_REFLECT_STRUCT(lsTextDocumentClientCapabilities,
synchronization, synchronization,
completion, completion,
hover, hover,
signatureHelp, signatureHelp,
references, references,
documentHighlight, documentHighlight,
documentSymbol, documentSymbol,
formatting, formatting,
rangeFormatting, rangeFormatting,
onTypeFormatting, onTypeFormatting,
definition, definition,
codeAction, codeAction,
codeLens, codeLens,
documentLink, documentLink,
rename); rename);
struct lsClientCapabilities { struct lsClientCapabilities {
// Workspace specific client capabilities. // Workspace specific client capabilities.
@ -840,8 +851,8 @@ struct lsClientCapabilities {
optional<lsTextDocumentClientCapabilities> textDocument; optional<lsTextDocumentClientCapabilities> textDocument;
/** /**
* Experimental client capabilities. * Experimental client capabilities.
*/ */
// experimental?: any; // TODO // experimental?: any; // TODO
}; };
MAKE_REFLECT_STRUCT(lsClientCapabilities, workspace, textDocument); MAKE_REFLECT_STRUCT(lsClientCapabilities, workspace, textDocument);
@ -849,7 +860,8 @@ MAKE_REFLECT_STRUCT(lsClientCapabilities, workspace, textDocument);
struct lsInitializeParams { struct lsInitializeParams {
// The process Id of the parent process that started // The process Id of the parent process that started
// the server. Is null if the process has not been started by another process. // the server. Is null if the process has not been started by another process.
// If the parent process is not alive then the server should exit (see exit notification) its process. // If the parent process is not alive then the server should exit (see exit
// notification) its process.
optional<int> processId; optional<int> processId;
// The rootPath of the workspace. Is null // The rootPath of the workspace. Is null
@ -871,9 +883,9 @@ struct lsInitializeParams {
enum class lsTrace { enum class lsTrace {
// NOTE: serialized as a string, one of 'off' | 'messages' | 'verbose'; // NOTE: serialized as a string, one of 'off' | 'messages' | 'verbose';
Off, // off Off, // off
Messages, // messages Messages, // messages
Verbose // verbose Verbose // verbose
}; };
// The initial trace setting. If omitted trace is disabled ('off'). // The initial trace setting. If omitted trace is disabled ('off').
@ -881,8 +893,13 @@ struct lsInitializeParams {
}; };
void Reflect(Reader& reader, lsInitializeParams::lsTrace& value); void Reflect(Reader& reader, lsInitializeParams::lsTrace& value);
void Reflect(Writer& writer, lsInitializeParams::lsTrace& value); void Reflect(Writer& writer, lsInitializeParams::lsTrace& value);
MAKE_REFLECT_STRUCT(lsInitializeParams, processId, rootPath, rootUri, initializationOptions, capabilities, trace); MAKE_REFLECT_STRUCT(lsInitializeParams,
processId,
rootPath,
rootUri,
initializationOptions,
capabilities,
trace);
struct lsInitializeError { struct lsInitializeError {
// Indicates whether the client should retry to send the // Indicates whether the client should retry to send the
@ -892,7 +909,8 @@ struct lsInitializeError {
}; };
MAKE_REFLECT_STRUCT(lsInitializeError, retry); MAKE_REFLECT_STRUCT(lsInitializeError, retry);
// Defines how the host (editor) should sync document changes to the language server. // Defines how the host (editor) should sync document changes to the language
// server.
enum class lsTextDocumentSyncKind { enum class lsTextDocumentSyncKind {
// Documents should not be synced at all. // Documents should not be synced at all.
None = 0, None = 0,
@ -941,7 +959,9 @@ struct lsDocumentOnTypeFormattingOptions {
// More trigger characters. // More trigger characters.
NonElidedVector<std::string> moreTriggerCharacter; NonElidedVector<std::string> moreTriggerCharacter;
}; };
MAKE_REFLECT_STRUCT(lsDocumentOnTypeFormattingOptions, firstTriggerCharacter, moreTriggerCharacter); MAKE_REFLECT_STRUCT(lsDocumentOnTypeFormattingOptions,
firstTriggerCharacter,
moreTriggerCharacter);
// Document link options // Document link options
struct lsDocumentLinkOptions { struct lsDocumentLinkOptions {
@ -967,8 +987,8 @@ MAKE_REFLECT_STRUCT(lsSaveOptions, includeText);
struct lsTextDocumentSyncOptions { struct lsTextDocumentSyncOptions {
// Open and close notifications are sent to the server. // Open and close notifications are sent to the server.
bool openClose = false; bool openClose = false;
// Change notificatins are sent to the server. See TextDocumentSyncKind.None, TextDocumentSyncKind.Full // Change notificatins are sent to the server. See TextDocumentSyncKind.None,
// and TextDocumentSyncKindIncremental. // TextDocumentSyncKind.Full and TextDocumentSyncKindIncremental.
lsTextDocumentSyncKind change = lsTextDocumentSyncKind::Incremental; lsTextDocumentSyncKind change = lsTextDocumentSyncKind::Incremental;
// Will save notifications are sent to the server. // Will save notifications are sent to the server.
optional<bool> willSave; optional<bool> willSave;
@ -977,11 +997,17 @@ struct lsTextDocumentSyncOptions {
// Save notifications are sent to the server. // Save notifications are sent to the server.
optional<lsSaveOptions> save; optional<lsSaveOptions> save;
}; };
MAKE_REFLECT_STRUCT(lsTextDocumentSyncOptions, openClose, change, willSave, willSaveWaitUntil, save); MAKE_REFLECT_STRUCT(lsTextDocumentSyncOptions,
openClose,
change,
willSave,
willSaveWaitUntil,
save);
struct lsServerCapabilities { struct lsServerCapabilities {
// Defines how text documents are synced. Is either a detailed structure defining each notification or // Defines how text documents are synced. Is either a detailed structure
// for backwards compatibility the TextDocumentSyncKind number. // defining each notification or for backwards compatibility the
// TextDocumentSyncKind number.
// TODO: It seems like the new API is broken and doesn't work. // TODO: It seems like the new API is broken and doesn't work.
// optional<lsTextDocumentSyncOptions> textDocumentSync; // optional<lsTextDocumentSyncOptions> textDocumentSync;
lsTextDocumentSyncKind textDocumentSync; lsTextDocumentSyncKind textDocumentSync;
@ -1020,23 +1046,23 @@ struct lsServerCapabilities {
optional<lsExecuteCommandOptions> executeCommandProvider; optional<lsExecuteCommandOptions> executeCommandProvider;
}; };
MAKE_REFLECT_STRUCT(lsServerCapabilities, MAKE_REFLECT_STRUCT(lsServerCapabilities,
textDocumentSync, textDocumentSync,
hoverProvider, hoverProvider,
completionProvider, completionProvider,
signatureHelpProvider, signatureHelpProvider,
definitionProvider, definitionProvider,
referencesProvider, referencesProvider,
documentHighlightProvider, documentHighlightProvider,
documentSymbolProvider, documentSymbolProvider,
workspaceSymbolProvider, workspaceSymbolProvider,
codeActionProvider, codeActionProvider,
codeLensProvider, codeLensProvider,
documentFormattingProvider, documentFormattingProvider,
documentRangeFormattingProvider, documentRangeFormattingProvider,
documentOnTypeFormattingProvider, documentOnTypeFormattingProvider,
renameProvider, renameProvider,
documentLinkProvider, documentLinkProvider,
executeCommandProvider); executeCommandProvider);
struct Ipc_InitializeRequest : public IpcMessage<Ipc_InitializeRequest> { struct Ipc_InitializeRequest : public IpcMessage<Ipc_InitializeRequest> {
const static IpcId kIpcId = IpcId::Initialize; const static IpcId kIpcId = IpcId::Initialize;
@ -1056,7 +1082,8 @@ struct Out_InitializeResponse : public lsOutMessage<Out_InitializeResponse> {
MAKE_REFLECT_STRUCT(Out_InitializeResponse::InitializeResult, capabilities); MAKE_REFLECT_STRUCT(Out_InitializeResponse::InitializeResult, capabilities);
MAKE_REFLECT_STRUCT(Out_InitializeResponse, jsonrpc, id, result); MAKE_REFLECT_STRUCT(Out_InitializeResponse, jsonrpc, id, result);
struct Ipc_InitializedNotification : public IpcMessage<Ipc_InitializedNotification> { struct Ipc_InitializedNotification
: public IpcMessage<Ipc_InitializedNotification> {
const static IpcId kIpcId = IpcId::Initialized; const static IpcId kIpcId = IpcId::Initialized;
lsRequestId id; lsRequestId id;
@ -1136,11 +1163,6 @@ struct Out_Error : public lsOutMessage<Out_Error> {
MAKE_REFLECT_STRUCT(Out_Error::lsResponseError, code, message); MAKE_REFLECT_STRUCT(Out_Error::lsResponseError, code, message);
MAKE_REFLECT_STRUCT(Out_Error, jsonrpc, id, error); MAKE_REFLECT_STRUCT(Out_Error, jsonrpc, id, error);
// Cancel an existing request. // Cancel an existing request.
struct Ipc_CancelRequest : public IpcMessage<Ipc_CancelRequest> { struct Ipc_CancelRequest : public IpcMessage<Ipc_CancelRequest> {
static const IpcId kIpcId = IpcId::CancelRequest; static const IpcId kIpcId = IpcId::CancelRequest;
@ -1148,10 +1170,6 @@ struct Ipc_CancelRequest : public IpcMessage<Ipc_CancelRequest> {
}; };
MAKE_REFLECT_STRUCT(Ipc_CancelRequest, id); MAKE_REFLECT_STRUCT(Ipc_CancelRequest, id);
// Open, update, close file // Open, update, close file
struct Ipc_TextDocumentDidOpen : public IpcMessage<Ipc_TextDocumentDidOpen> { struct Ipc_TextDocumentDidOpen : public IpcMessage<Ipc_TextDocumentDidOpen> {
struct Params { struct Params {
@ -1163,7 +1181,8 @@ struct Ipc_TextDocumentDidOpen : public IpcMessage<Ipc_TextDocumentDidOpen> {
}; };
MAKE_REFLECT_STRUCT(Ipc_TextDocumentDidOpen::Params, textDocument); MAKE_REFLECT_STRUCT(Ipc_TextDocumentDidOpen::Params, textDocument);
MAKE_REFLECT_STRUCT(Ipc_TextDocumentDidOpen, params); MAKE_REFLECT_STRUCT(Ipc_TextDocumentDidOpen, params);
struct Ipc_TextDocumentDidChange : public IpcMessage<Ipc_TextDocumentDidChange> { struct Ipc_TextDocumentDidChange
: public IpcMessage<Ipc_TextDocumentDidChange> {
struct lsTextDocumentContentChangeEvent { struct lsTextDocumentContentChangeEvent {
// The range of the document that changed. // The range of the document that changed.
lsRange range; lsRange range;
@ -1181,8 +1200,13 @@ struct Ipc_TextDocumentDidChange : public IpcMessage<Ipc_TextDocumentDidChange>
const static IpcId kIpcId = IpcId::TextDocumentDidChange; const static IpcId kIpcId = IpcId::TextDocumentDidChange;
Params params; Params params;
}; };
MAKE_REFLECT_STRUCT(Ipc_TextDocumentDidChange::lsTextDocumentContentChangeEvent, range, rangeLength, text); MAKE_REFLECT_STRUCT(Ipc_TextDocumentDidChange::lsTextDocumentContentChangeEvent,
MAKE_REFLECT_STRUCT(Ipc_TextDocumentDidChange::Params, textDocument, contentChanges); range,
rangeLength,
text);
MAKE_REFLECT_STRUCT(Ipc_TextDocumentDidChange::Params,
textDocument,
contentChanges);
MAKE_REFLECT_STRUCT(Ipc_TextDocumentDidChange, params); MAKE_REFLECT_STRUCT(Ipc_TextDocumentDidChange, params);
struct Ipc_TextDocumentDidClose : public IpcMessage<Ipc_TextDocumentDidClose> { struct Ipc_TextDocumentDidClose : public IpcMessage<Ipc_TextDocumentDidClose> {
struct Params { struct Params {
@ -1195,7 +1219,6 @@ struct Ipc_TextDocumentDidClose : public IpcMessage<Ipc_TextDocumentDidClose> {
MAKE_REFLECT_STRUCT(Ipc_TextDocumentDidClose::Params, textDocument); MAKE_REFLECT_STRUCT(Ipc_TextDocumentDidClose::Params, textDocument);
MAKE_REFLECT_STRUCT(Ipc_TextDocumentDidClose, params); MAKE_REFLECT_STRUCT(Ipc_TextDocumentDidClose, params);
struct Ipc_TextDocumentDidSave : public IpcMessage<Ipc_TextDocumentDidSave> { struct Ipc_TextDocumentDidSave : public IpcMessage<Ipc_TextDocumentDidSave> {
struct Params { struct Params {
// The document that was saved. // The document that was saved.
@ -1212,10 +1235,9 @@ struct Ipc_TextDocumentDidSave : public IpcMessage<Ipc_TextDocumentDidSave> {
MAKE_REFLECT_STRUCT(Ipc_TextDocumentDidSave::Params, textDocument); MAKE_REFLECT_STRUCT(Ipc_TextDocumentDidSave::Params, textDocument);
MAKE_REFLECT_STRUCT(Ipc_TextDocumentDidSave, params); MAKE_REFLECT_STRUCT(Ipc_TextDocumentDidSave, params);
// Diagnostics // Diagnostics
struct Out_TextDocumentPublishDiagnostics : public lsOutMessage<Out_TextDocumentPublishDiagnostics> { struct Out_TextDocumentPublishDiagnostics
: public lsOutMessage<Out_TextDocumentPublishDiagnostics> {
struct Params { struct Params {
// The URI for which diagnostic information is reported. // The URI for which diagnostic information is reported.
lsDocumentUri uri; lsDocumentUri uri;
@ -1226,7 +1248,7 @@ struct Out_TextDocumentPublishDiagnostics : public lsOutMessage<Out_TextDocument
Params params; Params params;
}; };
template<typename TVisitor> template <typename TVisitor>
void Reflect(TVisitor& visitor, Out_TextDocumentPublishDiagnostics& value) { void Reflect(TVisitor& visitor, Out_TextDocumentPublishDiagnostics& value) {
std::string method = "textDocument/publishDiagnostics"; std::string method = "textDocument/publishDiagnostics";
REFLECT_MEMBER_START(); REFLECT_MEMBER_START();
@ -1235,9 +1257,9 @@ void Reflect(TVisitor& visitor, Out_TextDocumentPublishDiagnostics& value) {
REFLECT_MEMBER(params); REFLECT_MEMBER(params);
REFLECT_MEMBER_END(); REFLECT_MEMBER_END();
} }
MAKE_REFLECT_STRUCT(Out_TextDocumentPublishDiagnostics::Params, uri, diagnostics); MAKE_REFLECT_STRUCT(Out_TextDocumentPublishDiagnostics::Params,
uri,
diagnostics);
// Rename // Rename
struct Ipc_TextDocumentRename : public IpcMessage<Ipc_TextDocumentRename> { struct Ipc_TextDocumentRename : public IpcMessage<Ipc_TextDocumentRename> {
@ -1258,7 +1280,10 @@ struct Ipc_TextDocumentRename : public IpcMessage<Ipc_TextDocumentRename> {
lsRequestId id; lsRequestId id;
Params params; Params params;
}; };
MAKE_REFLECT_STRUCT(Ipc_TextDocumentRename::Params, textDocument, position, newName); MAKE_REFLECT_STRUCT(Ipc_TextDocumentRename::Params,
textDocument,
position,
newName);
MAKE_REFLECT_STRUCT(Ipc_TextDocumentRename, id, params); MAKE_REFLECT_STRUCT(Ipc_TextDocumentRename, id, params);
struct Out_TextDocumentRename : public lsOutMessage<Out_TextDocumentRename> { struct Out_TextDocumentRename : public lsOutMessage<Out_TextDocumentRename> {
lsRequestId id; lsRequestId id;
@ -1266,10 +1291,6 @@ struct Out_TextDocumentRename : public lsOutMessage<Out_TextDocumentRename> {
}; };
MAKE_REFLECT_STRUCT(Out_TextDocumentRename, jsonrpc, id, result); MAKE_REFLECT_STRUCT(Out_TextDocumentRename, jsonrpc, id, result);
// Code completion // Code completion
struct Ipc_TextDocumentComplete : public IpcMessage<Ipc_TextDocumentComplete> { struct Ipc_TextDocumentComplete : public IpcMessage<Ipc_TextDocumentComplete> {
const static IpcId kIpcId = IpcId::TextDocumentCompletion; const static IpcId kIpcId = IpcId::TextDocumentCompletion;
@ -1286,14 +1307,16 @@ struct lsTextDocumentCompleteResult {
NonElidedVector<lsCompletionItem> items; NonElidedVector<lsCompletionItem> items;
}; };
MAKE_REFLECT_STRUCT(lsTextDocumentCompleteResult, isIncomplete, items); MAKE_REFLECT_STRUCT(lsTextDocumentCompleteResult, isIncomplete, items);
struct Out_TextDocumentComplete : public lsOutMessage<Out_TextDocumentComplete> { struct Out_TextDocumentComplete
: public lsOutMessage<Out_TextDocumentComplete> {
lsRequestId id; lsRequestId id;
lsTextDocumentCompleteResult result; lsTextDocumentCompleteResult result;
}; };
MAKE_REFLECT_STRUCT(Out_TextDocumentComplete, jsonrpc, id, result); MAKE_REFLECT_STRUCT(Out_TextDocumentComplete, jsonrpc, id, result);
// Signature help. // Signature help.
struct Ipc_TextDocumentSignatureHelp : public IpcMessage<Ipc_TextDocumentSignatureHelp> { struct Ipc_TextDocumentSignatureHelp
: public IpcMessage<Ipc_TextDocumentSignatureHelp> {
const static IpcId kIpcId = IpcId::TextDocumentSignatureHelp; const static IpcId kIpcId = IpcId::TextDocumentSignatureHelp;
lsRequestId id; lsRequestId id;
@ -1353,36 +1376,44 @@ struct lsSignatureHelp {
// active signature does have any. // active signature does have any.
optional<int> activeParameter; optional<int> activeParameter;
}; };
MAKE_REFLECT_STRUCT(lsSignatureHelp, signatures, activeSignature, activeParameter); MAKE_REFLECT_STRUCT(lsSignatureHelp,
struct Out_TextDocumentSignatureHelp : public lsOutMessage<Out_TextDocumentSignatureHelp> { signatures,
activeSignature,
activeParameter);
struct Out_TextDocumentSignatureHelp
: public lsOutMessage<Out_TextDocumentSignatureHelp> {
lsRequestId id; lsRequestId id;
lsSignatureHelp result; lsSignatureHelp result;
}; };
MAKE_REFLECT_STRUCT(Out_TextDocumentSignatureHelp, jsonrpc, id, result); MAKE_REFLECT_STRUCT(Out_TextDocumentSignatureHelp, jsonrpc, id, result);
// Goto definition // Goto definition
struct Ipc_TextDocumentDefinition : public IpcMessage<Ipc_TextDocumentDefinition> { struct Ipc_TextDocumentDefinition
: public IpcMessage<Ipc_TextDocumentDefinition> {
const static IpcId kIpcId = IpcId::TextDocumentDefinition; const static IpcId kIpcId = IpcId::TextDocumentDefinition;
lsRequestId id; lsRequestId id;
lsTextDocumentPositionParams params; lsTextDocumentPositionParams params;
}; };
MAKE_REFLECT_STRUCT(Ipc_TextDocumentDefinition, id, params); MAKE_REFLECT_STRUCT(Ipc_TextDocumentDefinition, id, params);
struct Out_TextDocumentDefinition : public lsOutMessage<Out_TextDocumentDefinition> { struct Out_TextDocumentDefinition
: public lsOutMessage<Out_TextDocumentDefinition> {
lsRequestId id; lsRequestId id;
NonElidedVector<lsLocation> result; NonElidedVector<lsLocation> result;
}; };
MAKE_REFLECT_STRUCT(Out_TextDocumentDefinition, jsonrpc, id, result); MAKE_REFLECT_STRUCT(Out_TextDocumentDefinition, jsonrpc, id, result);
// Document highlight // Document highlight
struct Ipc_TextDocumentDocumentHighlight : public IpcMessage<Ipc_TextDocumentDocumentHighlight> { struct Ipc_TextDocumentDocumentHighlight
: public IpcMessage<Ipc_TextDocumentDocumentHighlight> {
const static IpcId kIpcId = IpcId::TextDocumentDocumentHighlight; const static IpcId kIpcId = IpcId::TextDocumentDocumentHighlight;
lsRequestId id; lsRequestId id;
lsTextDocumentPositionParams params; lsTextDocumentPositionParams params;
}; };
MAKE_REFLECT_STRUCT(Ipc_TextDocumentDocumentHighlight, id, params); MAKE_REFLECT_STRUCT(Ipc_TextDocumentDocumentHighlight, id, params);
struct Out_TextDocumentDocumentHighlight : public lsOutMessage<Out_TextDocumentDocumentHighlight> { struct Out_TextDocumentDocumentHighlight
: public lsOutMessage<Out_TextDocumentDocumentHighlight> {
lsRequestId id; lsRequestId id;
NonElidedVector<lsDocumentHighlight> result; NonElidedVector<lsDocumentHighlight> result;
}; };
@ -1409,7 +1440,8 @@ MAKE_REFLECT_STRUCT(Out_TextDocumentHover::Result, contents, range);
MAKE_REFLECT_STRUCT(Out_TextDocumentHover, jsonrpc, id, result); MAKE_REFLECT_STRUCT(Out_TextDocumentHover, jsonrpc, id, result);
// References // References
struct Ipc_TextDocumentReferences : public IpcMessage<Ipc_TextDocumentReferences> { struct Ipc_TextDocumentReferences
: public IpcMessage<Ipc_TextDocumentReferences> {
struct lsReferenceContext { struct lsReferenceContext {
// Include the declaration of the current symbol. // Include the declaration of the current symbol.
bool includeDeclaration; bool includeDeclaration;
@ -1425,41 +1457,52 @@ struct Ipc_TextDocumentReferences : public IpcMessage<Ipc_TextDocumentReferences
lsRequestId id; lsRequestId id;
lsReferenceParams params; lsReferenceParams params;
}; };
MAKE_REFLECT_STRUCT(Ipc_TextDocumentReferences::lsReferenceContext, includeDeclaration); MAKE_REFLECT_STRUCT(Ipc_TextDocumentReferences::lsReferenceContext,
MAKE_REFLECT_STRUCT(Ipc_TextDocumentReferences::lsReferenceParams, textDocument, position, context); includeDeclaration);
MAKE_REFLECT_STRUCT(Ipc_TextDocumentReferences::lsReferenceParams,
textDocument,
position,
context);
MAKE_REFLECT_STRUCT(Ipc_TextDocumentReferences, id, params); MAKE_REFLECT_STRUCT(Ipc_TextDocumentReferences, id, params);
struct Out_TextDocumentReferences : public lsOutMessage<Out_TextDocumentReferences> { struct Out_TextDocumentReferences
: public lsOutMessage<Out_TextDocumentReferences> {
lsRequestId id; lsRequestId id;
NonElidedVector<lsLocation> result; NonElidedVector<lsLocation> result;
}; };
MAKE_REFLECT_STRUCT(Out_TextDocumentReferences, jsonrpc, id, result); MAKE_REFLECT_STRUCT(Out_TextDocumentReferences, jsonrpc, id, result);
// Code action // Code action
struct Ipc_TextDocumentCodeAction : public IpcMessage<Ipc_TextDocumentCodeAction> { struct Ipc_TextDocumentCodeAction
: public IpcMessage<Ipc_TextDocumentCodeAction> {
const static IpcId kIpcId = IpcId::TextDocumentCodeAction; const static IpcId kIpcId = IpcId::TextDocumentCodeAction;
// Contains additional diagnostic information about the context in which // Contains additional diagnostic information about the context in which
// a code action is run. // a code action is run.
struct lsCodeActionContext { struct lsCodeActionContext {
// An array of diagnostics. // An array of diagnostics.
NonElidedVector<lsDiagnostic> diagnostics; NonElidedVector<lsDiagnostic> diagnostics;
}; };
// Params for the CodeActionRequest // Params for the CodeActionRequest
struct lsCodeActionParams { struct lsCodeActionParams {
// The document in which the command was invoked. // The document in which the command was invoked.
lsTextDocumentIdentifier textDocument; lsTextDocumentIdentifier textDocument;
// The range for which the command was invoked. // The range for which the command was invoked.
lsRange range; lsRange range;
// Context carrying additional information. // Context carrying additional information.
lsCodeActionContext context; lsCodeActionContext context;
}; };
lsRequestId id; lsRequestId id;
lsCodeActionParams params; lsCodeActionParams params;
}; };
MAKE_REFLECT_STRUCT(Ipc_TextDocumentCodeAction::lsCodeActionContext, diagnostics); MAKE_REFLECT_STRUCT(Ipc_TextDocumentCodeAction::lsCodeActionContext,
MAKE_REFLECT_STRUCT(Ipc_TextDocumentCodeAction::lsCodeActionParams, textDocument, range, context); diagnostics);
MAKE_REFLECT_STRUCT(Ipc_TextDocumentCodeAction::lsCodeActionParams,
textDocument,
range,
context);
MAKE_REFLECT_STRUCT(Ipc_TextDocumentCodeAction, id, params); MAKE_REFLECT_STRUCT(Ipc_TextDocumentCodeAction, id, params);
struct Out_TextDocumentCodeAction : public lsOutMessage<Out_TextDocumentCodeAction> { struct Out_TextDocumentCodeAction
: public lsOutMessage<Out_TextDocumentCodeAction> {
struct CommandArgs { struct CommandArgs {
lsDocumentUri textDocumentUri; lsDocumentUri textDocumentUri;
NonElidedVector<lsTextEdit> edits; NonElidedVector<lsTextEdit> edits;
@ -1469,7 +1512,9 @@ struct Out_TextDocumentCodeAction : public lsOutMessage<Out_TextDocumentCodeActi
lsRequestId id; lsRequestId id;
NonElidedVector<Command> result; NonElidedVector<Command> result;
}; };
MAKE_REFLECT_STRUCT_WRITER_AS_ARRAY(Out_TextDocumentCodeAction::CommandArgs, textDocumentUri, edits); MAKE_REFLECT_STRUCT_WRITER_AS_ARRAY(Out_TextDocumentCodeAction::CommandArgs,
textDocumentUri,
edits);
MAKE_REFLECT_STRUCT(Out_TextDocumentCodeAction, jsonrpc, id, result); MAKE_REFLECT_STRUCT(Out_TextDocumentCodeAction, jsonrpc, id, result);
// List symbols in a document. // List symbols in a document.
@ -1477,21 +1522,24 @@ struct lsDocumentSymbolParams {
lsTextDocumentIdentifier textDocument; lsTextDocumentIdentifier textDocument;
}; };
MAKE_REFLECT_STRUCT(lsDocumentSymbolParams, textDocument); MAKE_REFLECT_STRUCT(lsDocumentSymbolParams, textDocument);
struct Ipc_TextDocumentDocumentSymbol : public IpcMessage<Ipc_TextDocumentDocumentSymbol> { struct Ipc_TextDocumentDocumentSymbol
: public IpcMessage<Ipc_TextDocumentDocumentSymbol> {
const static IpcId kIpcId = IpcId::TextDocumentDocumentSymbol; const static IpcId kIpcId = IpcId::TextDocumentDocumentSymbol;
lsRequestId id; lsRequestId id;
lsDocumentSymbolParams params; lsDocumentSymbolParams params;
}; };
MAKE_REFLECT_STRUCT(Ipc_TextDocumentDocumentSymbol, id, params); MAKE_REFLECT_STRUCT(Ipc_TextDocumentDocumentSymbol, id, params);
struct Out_TextDocumentDocumentSymbol : public lsOutMessage<Out_TextDocumentDocumentSymbol> { struct Out_TextDocumentDocumentSymbol
: public lsOutMessage<Out_TextDocumentDocumentSymbol> {
lsRequestId id; lsRequestId id;
NonElidedVector<lsSymbolInformation> result; NonElidedVector<lsSymbolInformation> result;
}; };
MAKE_REFLECT_STRUCT(Out_TextDocumentDocumentSymbol, jsonrpc, id, result); MAKE_REFLECT_STRUCT(Out_TextDocumentDocumentSymbol, jsonrpc, id, result);
// List links a document // List links a document
struct Ipc_TextDocumentDocumentLink : public IpcMessage<Ipc_TextDocumentDocumentLink> { struct Ipc_TextDocumentDocumentLink
: public IpcMessage<Ipc_TextDocumentDocumentLink> {
const static IpcId kIpcId = IpcId::TextDocumentDocumentLink; const static IpcId kIpcId = IpcId::TextDocumentDocumentLink;
struct DocumentLinkParams { struct DocumentLinkParams {
@ -1502,24 +1550,25 @@ struct Ipc_TextDocumentDocumentLink : public IpcMessage<Ipc_TextDocumentDocument
lsRequestId id; lsRequestId id;
DocumentLinkParams params; DocumentLinkParams params;
}; };
MAKE_REFLECT_STRUCT(Ipc_TextDocumentDocumentLink::DocumentLinkParams, textDocument); MAKE_REFLECT_STRUCT(Ipc_TextDocumentDocumentLink::DocumentLinkParams,
textDocument);
MAKE_REFLECT_STRUCT(Ipc_TextDocumentDocumentLink, id, params); MAKE_REFLECT_STRUCT(Ipc_TextDocumentDocumentLink, id, params);
// A document link is a range in a text document that links to an internal or external resource, like another // A document link is a range in a text document that links to an internal or
// text document or a web site. // external resource, like another text document or a web site.
struct lsDocumentLink { struct lsDocumentLink {
// The range this link applies to. // The range this link applies to.
lsRange range; lsRange range;
// The uri this link points to. If missing a resolve request is sent later. // The uri this link points to. If missing a resolve request is sent later.
optional<lsDocumentUri> target; optional<lsDocumentUri> target;
}; };
MAKE_REFLECT_STRUCT(lsDocumentLink, range, target); MAKE_REFLECT_STRUCT(lsDocumentLink, range, target);
struct Out_TextDocumentDocumentLink : public lsOutMessage<Out_TextDocumentDocumentLink> { struct Out_TextDocumentDocumentLink
: public lsOutMessage<Out_TextDocumentDocumentLink> {
lsRequestId id; lsRequestId id;
NonElidedVector<lsDocumentLink> result; NonElidedVector<lsDocumentLink> result;
}; };
MAKE_REFLECT_STRUCT(Out_TextDocumentDocumentLink, jsonrpc, id, result); MAKE_REFLECT_STRUCT(Out_TextDocumentDocumentLink, jsonrpc, id, result);
// List code lens in a document. // List code lens in a document.
struct lsDocumentCodeLensParams { struct lsDocumentCodeLensParams {
lsTextDocumentIdentifier textDocument; lsTextDocumentIdentifier textDocument;
@ -1541,9 +1590,11 @@ struct Ipc_TextDocumentCodeLens : public IpcMessage<Ipc_TextDocumentCodeLens> {
lsDocumentCodeLensParams params; lsDocumentCodeLensParams params;
}; };
MAKE_REFLECT_STRUCT(Ipc_TextDocumentCodeLens, id, params); MAKE_REFLECT_STRUCT(Ipc_TextDocumentCodeLens, id, params);
struct Out_TextDocumentCodeLens : public lsOutMessage<Out_TextDocumentCodeLens> { struct Out_TextDocumentCodeLens
: public lsOutMessage<Out_TextDocumentCodeLens> {
lsRequestId id; lsRequestId id;
NonElidedVector<lsCodeLens<lsCodeLensUserData, lsCodeLensCommandArguments>> result; NonElidedVector<lsCodeLens<lsCodeLensUserData, lsCodeLensCommandArguments>>
result;
}; };
MAKE_REFLECT_STRUCT(Out_TextDocumentCodeLens, jsonrpc, id, result); MAKE_REFLECT_STRUCT(Out_TextDocumentCodeLens, jsonrpc, id, result);
struct Ipc_CodeLensResolve : public IpcMessage<Ipc_CodeLensResolve> { struct Ipc_CodeLensResolve : public IpcMessage<Ipc_CodeLensResolve> {
@ -1577,12 +1628,7 @@ struct Out_WorkspaceSymbol : public lsOutMessage<Out_WorkspaceSymbol> {
MAKE_REFLECT_STRUCT(Out_WorkspaceSymbol, jsonrpc, id, result); MAKE_REFLECT_STRUCT(Out_WorkspaceSymbol, jsonrpc, id, result);
// Show a message to the user. // Show a message to the user.
enum class lsMessageType : int { enum class lsMessageType : int { Error = 1, Warning = 2, Info = 3, Log = 4 };
Error = 1,
Warning = 2,
Info = 3,
Log = 4
};
MAKE_REFLECT_TYPE_PROXY(lsMessageType, int) MAKE_REFLECT_TYPE_PROXY(lsMessageType, int)
struct Out_ShowLogMessageParams { struct Out_ShowLogMessageParams {
lsMessageType type = lsMessageType::Error; lsMessageType type = lsMessageType::Error;
@ -1590,15 +1636,13 @@ struct Out_ShowLogMessageParams {
}; };
MAKE_REFLECT_STRUCT(Out_ShowLogMessageParams, type, message); MAKE_REFLECT_STRUCT(Out_ShowLogMessageParams, type, message);
struct Out_ShowLogMessage : public lsOutMessage<Out_ShowLogMessage> { struct Out_ShowLogMessage : public lsOutMessage<Out_ShowLogMessage> {
enum class DisplayType { enum class DisplayType { Show, Log };
Show, Log
};
DisplayType display_type = DisplayType::Show; DisplayType display_type = DisplayType::Show;
std::string method(); std::string method();
Out_ShowLogMessageParams params; Out_ShowLogMessageParams params;
}; };
template<typename TVisitor> template <typename TVisitor>
void Reflect(TVisitor& visitor, Out_ShowLogMessage& value) { void Reflect(TVisitor& visitor, Out_ShowLogMessage& value) {
REFLECT_MEMBER_START(); REFLECT_MEMBER_START();
REFLECT_MEMBER(jsonrpc); REFLECT_MEMBER(jsonrpc);
@ -1608,8 +1652,8 @@ void Reflect(TVisitor& visitor, Out_ShowLogMessage& value) {
REFLECT_MEMBER_END(); REFLECT_MEMBER_END();
} }
struct Out_CquerySetInactiveRegion
struct Out_CquerySetInactiveRegion : public lsOutMessage<Out_CquerySetInactiveRegion> { : public lsOutMessage<Out_CquerySetInactiveRegion> {
struct Params { struct Params {
lsDocumentUri uri; lsDocumentUri uri;
NonElidedVector<lsRange> inactiveRegions; NonElidedVector<lsRange> inactiveRegions;
@ -1620,7 +1664,6 @@ struct Out_CquerySetInactiveRegion : public lsOutMessage<Out_CquerySetInactiveRe
MAKE_REFLECT_STRUCT(Out_CquerySetInactiveRegion::Params, uri, inactiveRegions); MAKE_REFLECT_STRUCT(Out_CquerySetInactiveRegion::Params, uri, inactiveRegions);
MAKE_REFLECT_STRUCT(Out_CquerySetInactiveRegion, jsonrpc, method, params); 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;
@ -1628,13 +1671,15 @@ struct Ipc_CqueryFreshenIndex : public IpcMessage<Ipc_CqueryFreshenIndex> {
MAKE_REFLECT_STRUCT(Ipc_CqueryFreshenIndex, id); MAKE_REFLECT_STRUCT(Ipc_CqueryFreshenIndex, id);
// Type Hierarchy Tree // Type Hierarchy Tree
struct Ipc_CqueryTypeHierarchyTree : public IpcMessage<Ipc_CqueryTypeHierarchyTree> { struct Ipc_CqueryTypeHierarchyTree
: public IpcMessage<Ipc_CqueryTypeHierarchyTree> {
const static IpcId kIpcId = IpcId::CqueryTypeHierarchyTree; const static IpcId kIpcId = IpcId::CqueryTypeHierarchyTree;
lsRequestId id; lsRequestId id;
lsTextDocumentPositionParams params; lsTextDocumentPositionParams params;
}; };
MAKE_REFLECT_STRUCT(Ipc_CqueryTypeHierarchyTree, id, params); MAKE_REFLECT_STRUCT(Ipc_CqueryTypeHierarchyTree, id, params);
struct Out_CqueryTypeHierarchyTree : public lsOutMessage<Out_CqueryTypeHierarchyTree> { struct Out_CqueryTypeHierarchyTree
: public lsOutMessage<Out_CqueryTypeHierarchyTree> {
struct TypeEntry { struct TypeEntry {
std::string name; std::string name;
optional<lsLocation> location; optional<lsLocation> location;
@ -1643,11 +1688,15 @@ struct Out_CqueryTypeHierarchyTree : public lsOutMessage<Out_CqueryTypeHierarchy
lsRequestId id; lsRequestId id;
optional<TypeEntry> result; optional<TypeEntry> result;
}; };
MAKE_REFLECT_STRUCT(Out_CqueryTypeHierarchyTree::TypeEntry, name, location, children); MAKE_REFLECT_STRUCT(Out_CqueryTypeHierarchyTree::TypeEntry,
name,
location,
children);
MAKE_REFLECT_STRUCT(Out_CqueryTypeHierarchyTree, jsonrpc, id, result); MAKE_REFLECT_STRUCT(Out_CqueryTypeHierarchyTree, jsonrpc, id, result);
// Call Tree // Call Tree
struct Ipc_CqueryCallTreeInitial : public IpcMessage<Ipc_CqueryCallTreeInitial> { struct Ipc_CqueryCallTreeInitial
: public IpcMessage<Ipc_CqueryCallTreeInitial> {
const static IpcId kIpcId = IpcId::CqueryCallTreeInitial; const static IpcId kIpcId = IpcId::CqueryCallTreeInitial;
lsRequestId id; lsRequestId id;
lsTextDocumentPositionParams params; lsTextDocumentPositionParams params;
@ -1664,9 +1713,7 @@ struct Ipc_CqueryCallTreeExpand : public IpcMessage<Ipc_CqueryCallTreeExpand> {
MAKE_REFLECT_STRUCT(Ipc_CqueryCallTreeExpand::Params, usr); MAKE_REFLECT_STRUCT(Ipc_CqueryCallTreeExpand::Params, usr);
MAKE_REFLECT_STRUCT(Ipc_CqueryCallTreeExpand, id, params); MAKE_REFLECT_STRUCT(Ipc_CqueryCallTreeExpand, id, params);
struct Out_CqueryCallTree : public lsOutMessage<Out_CqueryCallTree> { struct Out_CqueryCallTree : public lsOutMessage<Out_CqueryCallTree> {
enum class CallType { enum class CallType { Direct = 0, Base = 1, Derived = 2 };
Direct = 0, Base = 1, Derived = 2
};
struct CallEntry { struct CallEntry {
std::string name; std::string name;
std::string usr; std::string usr;
@ -1679,7 +1726,12 @@ struct Out_CqueryCallTree : public lsOutMessage<Out_CqueryCallTree> {
NonElidedVector<CallEntry> result; NonElidedVector<CallEntry> result;
}; };
MAKE_REFLECT_TYPE_PROXY(Out_CqueryCallTree::CallType, int); MAKE_REFLECT_TYPE_PROXY(Out_CqueryCallTree::CallType, int);
MAKE_REFLECT_STRUCT(Out_CqueryCallTree::CallEntry, name, usr, location, hasCallers, callType); MAKE_REFLECT_STRUCT(Out_CqueryCallTree::CallEntry,
name,
usr,
location,
hasCallers,
callType);
MAKE_REFLECT_STRUCT(Out_CqueryCallTree, jsonrpc, id, result); MAKE_REFLECT_STRUCT(Out_CqueryCallTree, jsonrpc, id, result);
// Vars, Callers, Derived, GotoParent // Vars, Callers, Derived, GotoParent

View File

@ -20,7 +20,9 @@ int GetOffsetForPosition(lsPosition position, const std::string& content) {
return std::min<int>(offset + position.character, content.size()); return std::min<int>(offset + position.character, content.size());
} }
lsPosition CharPos(const std::string& search, char character, int character_offset) { lsPosition CharPos(const std::string& search,
char character,
int character_offset) {
lsPosition result; lsPosition result;
int index = 0; int index = 0;
while (index < search.size()) { while (index < search.size()) {
@ -30,8 +32,7 @@ lsPosition CharPos(const std::string& search, char character, int character_offs
if (c == '\n') { if (c == '\n') {
result.line += 1; result.line += 1;
result.character = 0; result.character = 0;
} } else {
else {
result.character += 1; result.character += 1;
} }
++index; ++index;
@ -75,7 +76,11 @@ optional<lsRange> ExtractQuotedRange(int line_number, const std::string& line) {
return lsRange(lsPosition(line_number, start), lsPosition(line_number, end)); return lsRange(lsPosition(line_number, start), lsPosition(line_number, end));
} }
void LexFunctionDeclaration(const std::string& buffer_content, lsPosition declaration_spelling, optional<std::string> type_name, std::string* insert_text, int* newlines_after_name) { void LexFunctionDeclaration(const std::string& buffer_content,
lsPosition declaration_spelling,
optional<std::string> type_name,
std::string* insert_text,
int* newlines_after_name) {
int name_start = GetOffsetForPosition(declaration_spelling, buffer_content); int name_start = GetOffsetForPosition(declaration_spelling, buffer_content);
bool parse_return_type = true; bool parse_return_type = true;
@ -89,7 +94,8 @@ void LexFunctionDeclaration(const std::string& buffer_content, lsPosition declar
++name_end; ++name_end;
} }
std::string func_name = buffer_content.substr(name_start, name_end - name_start); std::string func_name =
buffer_content.substr(name_start, name_end - name_start);
if (func_name == *type_name || func_name == ("~" + *type_name)) if (func_name == *type_name || func_name == ("~" + *type_name))
parse_return_type = false; parse_return_type = false;
} }
@ -163,8 +169,7 @@ std::string LexWordAroundPos(lsPosition position, const std::string& content) {
char c = content[start - 1]; char c = content[start - 1];
if (isalnum(c) || c == '_') { if (isalnum(c) || c == '_') {
--start; --start;
} } else {
else {
break; break;
} }
} }
@ -173,8 +178,7 @@ std::string LexWordAroundPos(lsPosition position, const std::string& content) {
char c = content[end + 1]; char c = content[end + 1];
if (isalnum(c) || c == '_') { if (isalnum(c) || c == '_') {
++end; ++end;
} } else {
else {
break; break;
} }
} }

View File

@ -4,18 +4,23 @@
#include <string> #include <string>
// Utility method to map |position| to an offset inside of |content|. // Utility method to map |position| to an offset inside of |content|.
int GetOffsetForPosition(lsPosition position, const std::string& content); int GetOffsetForPosition(lsPosition position, const std::string& content);
// Utility method to find a position for the given character. // Utility method to find a position for the given character.
lsPosition CharPos(const std::string& search, char character, int character_offset = 0); lsPosition CharPos(const std::string& search,
char character,
int character_offset = 0);
bool ShouldRunIncludeCompletion(const std::string& line); bool ShouldRunIncludeCompletion(const std::string& line);
// TODO: eliminate |line_number| param. // TODO: eliminate |line_number| param.
optional<lsRange> ExtractQuotedRange(int line_number, const std::string& line); optional<lsRange> ExtractQuotedRange(int line_number, const std::string& line);
void LexFunctionDeclaration(const std::string& buffer_content, lsPosition declaration_spelling, optional<std::string> type_name, std::string* insert_text, int* newlines_after_name); void LexFunctionDeclaration(const std::string& buffer_content,
lsPosition declaration_spelling,
optional<std::string> type_name,
std::string* insert_text,
int* newlines_after_name);
std::string LexWordAroundPos(lsPosition position, const std::string& content); std::string LexWordAroundPos(lsPosition position, const std::string& content);

View File

@ -2,8 +2,9 @@
#define CURSOR_H_ #define CURSOR_H_
#include <string> #include <string>
#include <vector>
#include <type_traits> #include <type_traits>
#include <vector>
#include <clang-c/Index.h> #include <clang-c/Index.h>

View File

@ -1,13 +1,15 @@
#include "TranslationUnit.h" #include "TranslationUnit.h"
#include "Utility.h"
#include "../platform.h" #include "../platform.h"
#include "../utils.h" #include "../utils.h"
#include "Utility.h"
#include <fstream>
#include <sstream>
#include <cassert> #include <cassert>
#include <fstream>
#include <iostream> #include <iostream>
#include <sstream>
namespace clang { namespace clang {
@ -24,10 +26,12 @@ TranslationUnit::TranslationUnit(Index* index,
for (const auto& arg : platform_args) for (const auto& arg : platform_args)
args.push_back(arg.c_str()); args.push_back(arg.c_str());
//std::cerr << "Parsing " << filepath << " with args " << StringJoin(args) << std::endl; // std::cerr << "Parsing " << filepath << " with args " << StringJoin(args) <<
// std::endl;
//cx_tu = clang_createTranslationUnitFromSourceFile( // cx_tu = clang_createTranslationUnitFromSourceFile(
// index->cx_index, filepath.c_str(), args.size(), args.data(), (unsigned)unsaved_files.size(), unsaved_files.data()); // index->cx_index, filepath.c_str(), args.size(), args.data(),
// (unsigned)unsaved_files.size(), unsaved_files.data());
CXErrorCode error_code = clang_parseTranslationUnit2( CXErrorCode error_code = clang_parseTranslationUnit2(
index->cx_index, filepath.c_str(), args.data(), (int)args.size(), index->cx_index, filepath.c_str(), args.data(), (int)args.size(),
@ -38,20 +42,23 @@ TranslationUnit::TranslationUnit(Index* index,
did_fail = false; did_fail = false;
break; break;
case CXError_Failure: case CXError_Failure:
std::cerr << "libclang generic failure for " << filepath << " with args " << StringJoin(args) << std::endl; std::cerr << "libclang generic failure for " << filepath << " with args "
<< StringJoin(args) << std::endl;
did_fail = true; did_fail = true;
break; break;
case CXError_Crashed: case CXError_Crashed:
std::cerr << "libclang crashed for " << filepath << " with args " << StringJoin(args) << std::endl; std::cerr << "libclang crashed for " << filepath << " with args "
<< StringJoin(args) << std::endl;
did_fail = true; did_fail = true;
break; break;
case CXError_InvalidArguments: case CXError_InvalidArguments:
std::cerr << "libclang had invalid arguments for " << " with args " << StringJoin(args) << filepath std::cerr << "libclang had invalid arguments for "
<< std::endl; << " with args " << StringJoin(args) << filepath << std::endl;
did_fail = true; did_fail = true;
break; break;
case CXError_ASTReadError: case CXError_ASTReadError:
std::cerr << "libclang had ast read error for " << filepath << " with args " << StringJoin(args) << std::endl; std::cerr << "libclang had ast read error for " << filepath
<< " with args " << StringJoin(args) << std::endl;
did_fail = true; did_fail = true;
break; break;
} }
@ -63,9 +70,9 @@ TranslationUnit::~TranslationUnit() {
void TranslationUnit::ReparseTranslationUnit( void TranslationUnit::ReparseTranslationUnit(
std::vector<CXUnsavedFile>& unsaved) { std::vector<CXUnsavedFile>& unsaved) {
int error_code = int error_code = clang_reparseTranslationUnit(
clang_reparseTranslationUnit(cx_tu, (unsigned)unsaved.size(), unsaved.data(), cx_tu, (unsigned)unsaved.size(), unsaved.data(),
clang_defaultReparseOptions(cx_tu)); clang_defaultReparseOptions(cx_tu));
switch (error_code) { switch (error_code) {
case CXError_Success: case CXError_Success:
did_fail = false; did_fail = false;
@ -92,4 +99,4 @@ void TranslationUnit::ReparseTranslationUnit(
Cursor TranslationUnit::document_cursor() const { Cursor TranslationUnit::document_cursor() const {
return Cursor(clang_getTranslationUnitCursor(cx_tu)); return Cursor(clang_getTranslationUnitCursor(cx_tu));
} }
} } // namespace clang

View File

@ -1,8 +1,8 @@
#pragma once #pragma once
#include "Index.h"
#include "Cursor.h" #include "Cursor.h"
#include "Index.h"
#include <clang-c/Index.h> #include <clang-c/Index.h>

View File

@ -21,33 +21,31 @@ optional<Matcher> Matcher::Create(const std::string& search) {
try { try {
Matcher m; Matcher m;
m.regex_string = search; m.regex_string = search;
m.regex = std::regex(search, m.regex = std::regex(
std::regex_constants::ECMAScript | search, std::regex_constants::ECMAScript | std::regex_constants::icase |
std::regex_constants::icase | std::regex_constants::optimize
std::regex_constants::optimize // std::regex_constants::nosubs
//std::regex_constants::nosubs
); );
return m; return m;
} } catch (std::exception e) {
catch (std::exception e) {
Out_ShowLogMessage out; Out_ShowLogMessage out;
out.display_type = Out_ShowLogMessage::DisplayType::Show; out.display_type = Out_ShowLogMessage::DisplayType::Show;
out.params.type = lsMessageType::Error; out.params.type = lsMessageType::Error;
out.params.message = "cquery: Parsing EMCAScript regex \"" + search + "\" failed; " + e.what(); out.params.message = "cquery: Parsing EMCAScript regex \"" + search +
"\" failed; " + e.what();
IpcManager::instance()->SendOutMessageToClient(IpcId::Cout, out); IpcManager::instance()->SendOutMessageToClient(IpcId::Cout, out);
return nullopt; return nullopt;
} }
} }
bool Matcher::IsMatch(const std::string& value) const { bool Matcher::IsMatch(const std::string& value) const {
//std::smatch match; // std::smatch match;
//return std::regex_match(value, match, regex); // return std::regex_match(value, match, regex);
return std::regex_match(value, regex, std::regex_constants::match_any); return std::regex_match(value, regex, std::regex_constants::match_any);
} }
GroupMatch::GroupMatch( GroupMatch::GroupMatch(const std::vector<std::string>& whitelist,
const std::vector<std::string>& whitelist, const std::vector<std::string>& blacklist) {
const std::vector<std::string>& blacklist) {
for (const std::string& entry : whitelist) { for (const std::string& entry : whitelist) {
optional<Matcher> m = Matcher::Create(entry); optional<Matcher> m = Matcher::Create(entry);
if (m) if (m)
@ -60,7 +58,8 @@ GroupMatch::GroupMatch(
} }
} }
bool GroupMatch::IsMatch(const std::string& value, std::string* match_failure_reason) const { bool GroupMatch::IsMatch(const std::string& value,
std::string* match_failure_reason) const {
for (const Matcher& m : whitelist) { for (const Matcher& m : whitelist) {
if (!m.IsMatch(value)) { if (!m.IsMatch(value)) {
if (match_failure_reason) if (match_failure_reason)
@ -80,17 +79,16 @@ bool GroupMatch::IsMatch(const std::string& value, std::string* match_failure_re
return true; return true;
} }
TEST_SUITE("Matcher"); TEST_SUITE("Matcher");
TEST_CASE("sanity") { TEST_CASE("sanity") {
//Matcher m("abc"); // Matcher m("abc");
// TODO: check case // TODO: check case
//CHECK(m.IsMatch("abc")); // CHECK(m.IsMatch("abc"));
//CHECK(m.IsMatch("fooabc")); // CHECK(m.IsMatch("fooabc"));
//CHECK(m.IsMatch("abc")); // CHECK(m.IsMatch("abc"));
//CHECK(m.IsMatch("abcfoo")); // CHECK(m.IsMatch("abcfoo"));
//CHECK(m.IsMatch("11a11b11c11")); // CHECK(m.IsMatch("11a11b11c11"));
} }
TEST_SUITE_END(); TEST_SUITE_END();

View File

@ -6,8 +6,8 @@
#include <string> #include <string>
#include <vector> #include <vector>
using std::experimental::optional;
using std::experimental::nullopt; using std::experimental::nullopt;
using std::experimental::optional;
struct Matcher { struct Matcher {
static optional<Matcher> Create(const std::string& search); static optional<Matcher> Create(const std::string& search);
@ -23,7 +23,8 @@ struct GroupMatch {
GroupMatch(const std::vector<std::string>& whitelist, GroupMatch(const std::vector<std::string>& whitelist,
const std::vector<std::string>& blacklist); const std::vector<std::string>& blacklist);
bool IsMatch(const std::string& value, std::string* match_failure_reason = nullptr) const; bool IsMatch(const std::string& value,
std::string* match_failure_reason = nullptr) const;
std::vector<Matcher> whitelist; std::vector<Matcher> whitelist;
std::vector<Matcher> blacklist; std::vector<Matcher> blacklist;

View File

@ -162,7 +162,6 @@ MessageQueue::MessageQueue(std::unique_ptr<Buffer> buffer, bool buffer_has_data)
local_buffer_ = Buffer::Create(buffer_->capacity - sizeof(BufferMetadata)); local_buffer_ = Buffer::Create(buffer_->capacity - sizeof(BufferMetadata));
memset(local_buffer_->data, 0, local_buffer_->capacity); memset(local_buffer_->data, 0, local_buffer_->capacity);
} }
void MessageQueue::Enqueue(const Message& message) { void MessageQueue::Enqueue(const Message& message) {

View File

@ -1,8 +1,9 @@
#pragma once #pragma once
#include <vector>
#include <memory> #include <memory>
#include <unordered_map> #include <unordered_map>
#include <vector>
#include "buffer.h" #include "buffer.h"

View File

@ -3,7 +3,7 @@
#include <iostream> #include <iostream>
std::unordered_map<std::string, std::string> ParseOptions(int argc, std::unordered_map<std::string, std::string> ParseOptions(int argc,
char** argv) { char** argv) {
std::unordered_map<std::string, std::string> output; std::unordered_map<std::string, std::string> output;
std::string previous_arg; std::string previous_arg;
@ -14,14 +14,13 @@ std::unordered_map<std::string, std::string> ParseOptions(int argc,
if (arg[0] != '-') { if (arg[0] != '-') {
if (previous_arg.size() == 0) { if (previous_arg.size() == 0) {
std::cerr << "Invalid arguments; switches must start with -" std::cerr << "Invalid arguments; switches must start with -"
<< std::endl; << std::endl;
exit(1); exit(1);
} }
output[previous_arg] = arg; output[previous_arg] = arg;
previous_arg = ""; previous_arg = "";
} } else {
else {
output[arg] = ""; output[arg] = "";
previous_arg = arg; previous_arg = arg;
} }
@ -31,6 +30,6 @@ std::unordered_map<std::string, std::string> ParseOptions(int argc,
} }
bool HasOption(const std::unordered_map<std::string, std::string>& options, bool HasOption(const std::unordered_map<std::string, std::string>& options,
const std::string& option) { const std::string& option) {
return options.find(option) != options.end(); return options.find(option) != options.end();
} }

View File

@ -2,10 +2,8 @@
#include <unordered_map> #include <unordered_map>
std::unordered_map<std::string, std::string> ParseOptions( std::unordered_map<std::string, std::string> ParseOptions(int argc,
int argc, char** argv);
char** argv);
bool HasOption( bool HasOption(const std::unordered_map<std::string, std::string>& options,
const std::unordered_map<std::string, std::string>& options, const std::string& option);
const std::string& option);

View File

@ -22,8 +22,14 @@ struct PerformanceImportFile {
// [indexer] create delta IndexUpdate object // [indexer] create delta IndexUpdate object
uint64_t index_make_delta = 0; uint64_t index_make_delta = 0;
// [querydb] update WorkingFile indexed file state // [querydb] update WorkingFile indexed file state
//uint64_t querydb_update_working_file = 0; // uint64_t querydb_update_working_file = 0;
// [querydb] apply IndexUpdate // [querydb] apply IndexUpdate
//uint64_t querydb_apply_index_update = 0; // uint64_t querydb_apply_index_update = 0;
}; };
MAKE_REFLECT_STRUCT(PerformanceImportFile, index_parse, index_build, querydb_id_map, index_save_to_disk, index_load_cached, index_make_delta); MAKE_REFLECT_STRUCT(PerformanceImportFile,
index_parse,
index_build,
querydb_id_map,
index_save_to_disk,
index_load_cached,
index_make_delta);

View File

@ -2,18 +2,19 @@
#include <iostream> #include <iostream>
#include <iterator> #include <iterator>
#include <string>
#include <sstream> #include <sstream>
#include <string>
#include <thread> #include <thread>
#include <vector> #include <vector>
#include <doctest/doctest.h> #include <doctest/doctest.h>
namespace { namespace {
// See http://stackoverflow.com/a/236803 // See http://stackoverflow.com/a/236803
template<typename Out> template <typename Out>
void Split(const std::string &s, char delim, Out result) { void Split(const std::string& s, char delim, Out result) {
std::stringstream ss; std::stringstream ss;
ss.str(s); ss.str(s);
std::string item; std::string item;
@ -22,7 +23,7 @@ void Split(const std::string &s, char delim, Out result) {
*(result++) = item; *(result++) = item;
} }
} }
std::vector<std::string> Split(const std::string &s, char delim) { std::vector<std::string> Split(const std::string& s, char delim) {
std::vector<std::string> elems; std::vector<std::string> elems;
Split(s, delim, std::back_inserter(elems)); Split(s, delim, std::back_inserter(elems));
return elems; return elems;
@ -70,14 +71,16 @@ void MakeDirectoryRecursive(std::string path) {
} }
if (first_success == -1) { if (first_success == -1) {
std::cerr << "Failed to make any parent directory for " << path << std::endl; std::cerr << "Failed to make any parent directory for " << path
<< std::endl;
exit(1); exit(1);
} }
// Make all child directories. // Make all child directories.
for (int i = first_success + 1; i <= components.size(); ++i) { for (int i = first_success + 1; i <= components.size(); ++i) {
if (TryMakeDirectory(prefix + Join(components, '/', i)) == false) { if (TryMakeDirectory(prefix + Join(components, '/', i)) == false) {
std::cerr << "Failed making directory for " << path << " even after creating parent directories" << std::endl; std::cerr << "Failed making directory for " << path
<< " even after creating parent directories" << std::endl;
exit(1); exit(1);
} }
} }

View File

@ -6,8 +6,8 @@
#include <string> #include <string>
#include <vector> #include <vector>
using std::experimental::optional;
using std::experimental::nullopt; using std::experimental::nullopt;
using std::experimental::optional;
struct PlatformMutex { struct PlatformMutex {
virtual ~PlatformMutex(); virtual ~PlatformMutex();

View File

@ -6,34 +6,39 @@
#include <loguru.hpp> #include <loguru.hpp>
#include <malloc.h> #include <malloc.h>
#include <cassert>
#include <string>
#include <pthread.h> #include <pthread.h>
#include <cassert>
#include <iostream> #include <iostream>
#include <string>
#include <unistd.h>
#include <stdio.h>
#include <limits.h>
#include <string.h>
#include <stdlib.h>
#include <semaphore.h>
#include <time.h>
#include <assert.h> #include <assert.h>
#include <errno.h> #include <errno.h>
#include <limits.h>
#include <semaphore.h>
#include <signal.h> #include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <dirent.h> #include <dirent.h>
#include <sys/types.h> // required for stat.h
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/types.h> // required for stat.h
#include <errno.h>
#include <fcntl.h> #include <fcntl.h>
#include <unistd.h> #include <unistd.h>
#include <errno.h>
#include <fcntl.h> /* For O_* constants */
#include <sys/stat.h> /* For mode constants */ #include <fcntl.h> /* For O_* constants */
#include <semaphore.h> #include <semaphore.h>
#include <sys/mman.h> #include <sys/mman.h>
#include <sys/stat.h> /* For mode constants */
#ifndef __APPLE__ #ifndef __APPLE__
#include <sys/prctl.h> #include <sys/prctl.h>
@ -85,8 +90,9 @@ struct PlatformSharedMemoryLinux : public PlatformSharedMemory {
int fd_; int fd_;
PlatformSharedMemoryLinux(const std::string& name, size_t size) PlatformSharedMemoryLinux(const std::string& name, size_t size)
: name_(name), size_(size) { : name_(name), size_(size) {
std::cerr << "PlatformSharedMemoryLinux name=" << name << ", size=" << size << std::endl; std::cerr << "PlatformSharedMemoryLinux name=" << name << ", size=" << size
<< std::endl;
// Try to create shared memory but only if it does not already exist. Since // Try to create shared memory but only if it does not already exist. Since
// we created the memory, we need to initialize it. // we created the memory, we need to initialize it.
@ -105,9 +111,8 @@ struct PlatformSharedMemoryLinux : public PlatformSharedMemory {
} }
// Map the shared memory to an address. // Map the shared memory to an address.
data = data = CHECKED(mmap(nullptr /*kernel assigned starting address*/, size,
CHECKED(mmap(nullptr /*kernel assigned starting address*/, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd_, 0 /*offset*/));
PROT_READ | PROT_WRITE, MAP_SHARED, fd_, 0 /*offset*/));
capacity = size; capacity = size;
std::cerr << "Open shared memory name=" << name << ", fd=" << fd_ std::cerr << "Open shared memory name=" << name << ", fd=" << fd_
@ -134,13 +139,13 @@ std::unique_ptr<PlatformScopedMutexLock> CreatePlatformScopedMutexLock(
} }
std::unique_ptr<PlatformSharedMemory> CreatePlatformSharedMemory( std::unique_ptr<PlatformSharedMemory> CreatePlatformSharedMemory(
const std::string& name, size_t size) { const std::string& name,
size_t size) {
std::string name2 = "/" + name; std::string name2 = "/" + name;
return MakeUnique<PlatformSharedMemoryLinux>(name2, size); return MakeUnique<PlatformSharedMemoryLinux>(name2, size);
} }
void PlatformInit() { void PlatformInit() {}
}
std::string NormalizePath(const std::string& path) { std::string NormalizePath(const std::string& path) {
errno = 0; errno = 0;
@ -152,7 +157,7 @@ std::string NormalizePath(const std::string& path) {
} }
bool TryMakeDirectory(const std::string& absolute_path) { bool TryMakeDirectory(const std::string& absolute_path) {
const mode_t kMode = 0777; // UNIX style permissions const mode_t kMode = 0777; // UNIX style permissions
if (mkdir(absolute_path.c_str(), kMode) == -1) { if (mkdir(absolute_path.c_str(), kMode) == -1) {
// Success if the directory exists. // Success if the directory exists.
return errno == EEXIST; return errno == EEXIST;
@ -172,14 +177,16 @@ optional<int64_t> GetLastModificationTime(const std::string& absolute_path) {
if (stat(absolute_path.c_str(), &buf) != 0) { if (stat(absolute_path.c_str(), &buf) != 0) {
switch (errno) { switch (errno) {
case ENOENT: case ENOENT:
//std::cerr << "GetLastModificationTime: unable to find file " << absolute_path << std::endl; // std::cerr << "GetLastModificationTime: unable to find file " <<
// absolute_path << std::endl;
return nullopt; return nullopt;
case EINVAL: case EINVAL:
//std::cerr << "GetLastModificationTime: invalid param to _stat for file file " << absolute_path << std::endl; // std::cerr << "GetLastModificationTime: invalid param to _stat for
// file file " << absolute_path << std::endl;
return nullopt; return nullopt;
default: default:
//std::cerr << "GetLastModificationTime: unhandled for " << absolute_path << std::endl; // std::cerr << "GetLastModificationTime: unhandled for " <<
//exit(1); // absolute_path << std::endl; exit(1);
return nullopt; return nullopt;
} }
} }
@ -205,7 +212,7 @@ void CopyFileTo(const std::string& dest, const std::string& source) {
char buf[4096]; char buf[4096];
ssize_t nread; ssize_t nread;
while (nread = read(fd_from, buf, sizeof buf), nread > 0) { while (nread = read(fd_from, buf, sizeof buf), nread > 0) {
char *out_ptr = buf; char* out_ptr = buf;
ssize_t nwritten; ssize_t nwritten;
do { do {
@ -214,8 +221,7 @@ void CopyFileTo(const std::string& dest, const std::string& source) {
if (nwritten >= 0) { if (nwritten >= 0) {
nread -= nwritten; nread -= nwritten;
out_ptr += nwritten; out_ptr += nwritten;
} } else if (errno != EINTR)
else if (errno != EINTR)
goto out_error; goto out_error;
} while (nread > 0); } while (nread > 0);
} }

View File

@ -5,13 +5,15 @@
#include <loguru.hpp> #include <loguru.hpp>
#include <Windows.h>
#include <direct.h> #include <direct.h>
#include <fcntl.h> #include <fcntl.h>
#include <io.h> #include <io.h>
#include <Windows.h>
#include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/types.h>
#include <algorithm> #include <algorithm>
#include <cassert> #include <cassert>
@ -23,21 +25,21 @@ namespace {
DWORD CheckForError(std::vector<DWORD> allow) { DWORD CheckForError(std::vector<DWORD> allow) {
DWORD error = GetLastError(); DWORD error = GetLastError();
if (error == ERROR_SUCCESS || if (error == ERROR_SUCCESS ||
std::find(allow.begin(), allow.end(), error) != allow.end()) std::find(allow.begin(), allow.end(), error) != allow.end())
return error; return error;
// See http://stackoverflow.com/a/17387176 // See http://stackoverflow.com/a/17387176
LPSTR message_buffer = nullptr; LPSTR message_buffer = nullptr;
size_t size = FormatMessageA( size_t size = FormatMessageA(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS, FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), NULL, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPSTR)&message_buffer, 0, NULL); (LPSTR)&message_buffer, 0, NULL);
std::string message(message_buffer, size); std::string message(message_buffer, size);
LocalFree(message_buffer); LocalFree(message_buffer);
std::cerr << "Windows error code=" << error << ", message=" << message std::cerr << "Windows error code=" << error << ", message=" << message
<< std::endl; << std::endl;
assert(false); // debugger break assert(false); // debugger break
exit(1); exit(1);
@ -49,7 +51,7 @@ struct PlatformMutexWin : public PlatformMutex {
PlatformMutexWin(const std::string& name) { PlatformMutexWin(const std::string& name) {
std::cerr << "[win] Creating mutex with name " << name << std::endl; std::cerr << "[win] Creating mutex with name " << name << std::endl;
raw_mutex = CreateMutex(nullptr, false /*initial_owner*/, name.c_str()); raw_mutex = CreateMutex(nullptr, false /*initial_owner*/, name.c_str());
CheckForError({ ERROR_ALREADY_EXISTS }); CheckForError({ERROR_ALREADY_EXISTS});
} }
~PlatformMutexWin() override { ~PlatformMutexWin() override {
@ -65,12 +67,12 @@ struct PlatformScopedMutexLockWin : public PlatformScopedMutexLock {
PlatformScopedMutexLockWin(HANDLE raw_mutex) : raw_mutex(raw_mutex) { PlatformScopedMutexLockWin(HANDLE raw_mutex) : raw_mutex(raw_mutex) {
DWORD result = WaitForSingleObject(raw_mutex, INFINITE); DWORD result = WaitForSingleObject(raw_mutex, INFINITE);
assert(result == WAIT_OBJECT_0); assert(result == WAIT_OBJECT_0);
CheckForError({ ERROR_NO_MORE_FILES, ERROR_ALREADY_EXISTS } /*allow*/); CheckForError({ERROR_NO_MORE_FILES, ERROR_ALREADY_EXISTS} /*allow*/);
} }
~PlatformScopedMutexLockWin() override { ~PlatformScopedMutexLockWin() override {
ReleaseMutex(raw_mutex); ReleaseMutex(raw_mutex);
CheckForError({ ERROR_NO_MORE_FILES, ERROR_ALREADY_EXISTS } /*allow*/); CheckForError({ERROR_NO_MORE_FILES, ERROR_ALREADY_EXISTS} /*allow*/);
} }
}; };
@ -79,15 +81,15 @@ struct PlatformSharedMemoryWin : public PlatformSharedMemory {
PlatformSharedMemoryWin(const std::string& name, size_t capacity) { PlatformSharedMemoryWin(const std::string& name, size_t capacity) {
std::cerr << "[win] Creating shared memory with name " << name std::cerr << "[win] Creating shared memory with name " << name
<< " and capacity " << capacity << std::endl; << " and capacity " << capacity << std::endl;
this->name = name; this->name = name;
shmem_ = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, shmem_ = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0,
(DWORD)capacity, name.c_str()); (DWORD)capacity, name.c_str());
CheckForError({ ERROR_ALREADY_EXISTS } /*allow*/); CheckForError({ERROR_ALREADY_EXISTS} /*allow*/);
data = MapViewOfFile(shmem_, FILE_MAP_ALL_ACCESS, 0, 0, capacity); data = MapViewOfFile(shmem_, FILE_MAP_ALL_ACCESS, 0, 0, capacity);
CheckForError({ ERROR_ALREADY_EXISTS } /*allow*/); CheckForError({ERROR_ALREADY_EXISTS} /*allow*/);
this->capacity = capacity; this->capacity = capacity;
} }
@ -108,14 +110,14 @@ std::unique_ptr<PlatformMutex> CreatePlatformMutex(const std::string& name) {
} }
std::unique_ptr<PlatformScopedMutexLock> CreatePlatformScopedMutexLock( std::unique_ptr<PlatformScopedMutexLock> CreatePlatformScopedMutexLock(
PlatformMutex* mutex) { PlatformMutex* mutex) {
return MakeUnique<PlatformScopedMutexLockWin>( return MakeUnique<PlatformScopedMutexLockWin>(
static_cast<PlatformMutexWin*>(mutex)->raw_mutex); static_cast<PlatformMutexWin*>(mutex)->raw_mutex);
} }
std::unique_ptr<PlatformSharedMemory> CreatePlatformSharedMemory( std::unique_ptr<PlatformSharedMemory> CreatePlatformSharedMemory(
const std::string& name, const std::string& name,
size_t size) { size_t size) {
return MakeUnique<PlatformSharedMemoryWin>(name, size); return MakeUnique<PlatformSharedMemoryWin>(name, size);
} }
@ -137,7 +139,7 @@ std::string NormalizePath(const std::string& path) {
DWORD retval = 0; DWORD retval = 0;
TCHAR buffer[MAX_PATH] = TEXT(""); TCHAR buffer[MAX_PATH] = TEXT("");
TCHAR buf[MAX_PATH] = TEXT(""); TCHAR buf[MAX_PATH] = TEXT("");
TCHAR** lpp_part = { NULL }; TCHAR** lpp_part = {NULL};
retval = GetFullPathName(path.c_str(), MAX_PATH, buffer, lpp_part); retval = GetFullPathName(path.c_str(), MAX_PATH, buffer, lpp_part);
// fail, return original // fail, return original
@ -146,7 +148,7 @@ std::string NormalizePath(const std::string& path) {
std::string result = buffer; std::string result = buffer;
std::replace(result.begin(), result.end(), '\\', '/'); std::replace(result.begin(), result.end(), '\\', '/');
//std::transform(result.begin(), result.end(), result.begin(), ::tolower); // std::transform(result.begin(), result.end(), result.begin(), ::tolower);
return result; return result;
} }
@ -160,13 +162,12 @@ bool TryMakeDirectory(const std::string& absolute_path) {
// See https://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx // See https://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx
const DWORD MS_VC_EXCEPTION = 0x406D1388; const DWORD MS_VC_EXCEPTION = 0x406D1388;
#pragma pack(push,8) #pragma pack(push, 8)
typedef struct tagTHREADNAME_INFO typedef struct tagTHREADNAME_INFO {
{ DWORD dwType; // Must be 0x1000.
DWORD dwType; // Must be 0x1000. LPCSTR szName; // Pointer to name (in user addr space).
LPCSTR szName; // Pointer to name (in user addr space). DWORD dwThreadID; // Thread ID (-1=caller thread).
DWORD dwThreadID; // Thread ID (-1=caller thread). DWORD dwFlags; // Reserved for future use, must be zero.
DWORD dwFlags; // Reserved for future use, must be zero.
} THREADNAME_INFO; } THREADNAME_INFO;
#pragma pack(pop) #pragma pack(pop)
void SetCurrentThreadName(const std::string& thread_name) { void SetCurrentThreadName(const std::string& thread_name) {
@ -179,9 +180,10 @@ void SetCurrentThreadName(const std::string& thread_name) {
info.dwFlags = 0; info.dwFlags = 0;
__try { __try {
RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), (ULONG_PTR*)&info); RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR),
(ULONG_PTR*)&info);
} __except (EXCEPTION_EXECUTE_HANDLER) {
} }
__except (EXCEPTION_EXECUTE_HANDLER) {}
} }
optional<int64_t> GetLastModificationTime(const std::string& absolute_path) { optional<int64_t> GetLastModificationTime(const std::string& absolute_path) {
@ -189,14 +191,16 @@ optional<int64_t> GetLastModificationTime(const std::string& absolute_path) {
if (_stat(absolute_path.c_str(), &buf) != 0) { if (_stat(absolute_path.c_str(), &buf) != 0) {
switch (errno) { switch (errno) {
case ENOENT: case ENOENT:
//std::cerr << "GetLastModificationTime: unable to find file " << absolute_path << std::endl; // std::cerr << "GetLastModificationTime: unable to find file " <<
// absolute_path << std::endl;
return nullopt; return nullopt;
case EINVAL: case EINVAL:
//std::cerr << "GetLastModificationTime: invalid param to _stat for file file " << absolute_path << std::endl; // std::cerr << "GetLastModificationTime: invalid param to _stat for
// file file " << absolute_path << std::endl;
return nullopt; return nullopt;
default: default:
//std::cerr << "GetLastModificationTime: unhandled for " << absolute_path << std::endl; // std::cerr << "GetLastModificationTime: unhandled for " <<
//exit(1); // absolute_path << std::endl; exit(1);
return nullopt; return nullopt;
} }
} }
@ -205,15 +209,11 @@ optional<int64_t> GetLastModificationTime(const std::string& absolute_path) {
} }
void MoveFileTo(const std::string& destination, const std::string& source) { void MoveFileTo(const std::string& destination, const std::string& source) {
MoveFile(source.c_str(), MoveFile(source.c_str(), destination.c_str());
destination.c_str());
} }
void CopyFileTo(const std::string& destination, const std::string& source) { void CopyFileTo(const std::string& destination, const std::string& source) {
CopyFile( CopyFile(source.c_str(), destination.c_str(), false /*failIfExists*/);
source.c_str(),
destination.c_str(),
false /*failIfExists*/);
} }
bool IsSymLink(const std::string& path) { bool IsSymLink(const std::string& path) {
@ -221,10 +221,7 @@ bool IsSymLink(const std::string& path) {
} }
std::vector<std::string> GetPlatformClangArguments() { std::vector<std::string> GetPlatformClangArguments() {
return { return {"-fms-compatibility", "-fdelayed-template-parsing"};
"-fms-compatibility",
"-fdelayed-template-parsing"
};
} }
void FreeUnusedMemory() {} void FreeUnusedMemory() {}

View File

@ -12,8 +12,7 @@ const char* SkipAfter(const char* input, char skip_after) {
Position::Position() {} Position::Position() {}
Position::Position(int16_t line, int16_t column) Position::Position(int16_t line, int16_t column) : line(line), column(column) {}
: line(line), column(column) {}
Position::Position(const char* encoded) { Position::Position(const char* encoded) {
assert(encoded); assert(encoded);
@ -61,7 +60,9 @@ bool Position::operator==(const Position& that) const {
return line == that.line && column == that.column; return line == that.line && column == that.column;
} }
bool Position::operator!=(const Position& that) const { return !(*this == that); } bool Position::operator!=(const Position& that) const {
return !(*this == that);
}
bool Position::operator<(const Position& that) const { bool Position::operator<(const Position& that) const {
if (line < that.line) if (line < that.line)
@ -133,7 +134,9 @@ bool Range::operator==(const Range& that) const {
return start == that.start && end == that.end; return start == that.start && end == that.end;
} }
bool Range::operator!=(const Range& that) const { return !(*this == that); } bool Range::operator!=(const Range& that) const {
return !(*this == that);
}
bool Range::operator<(const Range& that) const { bool Range::operator<(const Range& that) const {
if (start < that.start) if (start < that.start)
@ -150,7 +153,6 @@ void Reflect(Writer& visitor, Position& value) {
visitor.String(output.c_str(), (rapidjson::SizeType)output.size()); visitor.String(output.c_str(), (rapidjson::SizeType)output.size());
} }
// Range // Range
void Reflect(Reader& visitor, Range& value) { void Reflect(Reader& visitor, Range& value) {
value = Range(visitor.GetString()); value = Range(visitor.GetString());

View File

@ -24,7 +24,9 @@ struct Position {
bool operator!=(const Position& that) const; bool operator!=(const Position& that) const;
bool operator<(const Position& that) const; bool operator<(const Position& that) const;
}; };
static_assert(sizeof(Position) == 4, "Investigate, Position should be 32-bits for indexer size reasons"); static_assert(
sizeof(Position) == 4,
"Investigate, Position should be 32-bits for indexer size reasons");
MAKE_HASHABLE(Position, t.line, t.column); MAKE_HASHABLE(Position, t.line, t.column);
struct Range { struct Range {

View File

@ -1,11 +1,12 @@
#include "project.h" #include "project.h"
#include "match.h"
#include "libclangmm/Utility.h" #include "libclangmm/Utility.h"
#include "match.h"
#include "platform.h" #include "platform.h"
#include "serializer.h" #include "serializer.h"
#include "utils.h" #include "utils.h"
#include <clang-c/CXCompilationDatabase.h> #include <clang-c/CXCompilationDatabase.h>
#include <doctest/doctest.h> #include <doctest/doctest.h>
#include <loguru.hpp> #include <loguru.hpp>
@ -26,79 +27,40 @@ MAKE_REFLECT_STRUCT(CompileCommandsEntry, directory, file, command, args);
namespace { namespace {
static const char* kBlacklistMulti[] = {"-MF", "-Xclang"};
static const char* kBlacklistMulti[] = {
"-MF",
"-Xclang"
};
// Blacklisted flags which are always removed from the command line. // Blacklisted flags which are always removed from the command line.
static const char *kBlacklist[] = { static const char* kBlacklist[] = {
"--param", "--param", "-M", "-MD", "-MG", "-MM", "-MMD", "-MP", "-MQ", "-MT", "-Og",
"-M", "-Wa,--32", "-Wa,--64", "-Wl,--incremental-full",
"-MD", "-Wl,--incremental-patch,1", "-Wl,--no-incremental",
"-MG", "-fbuild-session-file=", "-fbuild-session-timestamp=", "-fembed-bitcode",
"-MM", "-fembed-bitcode-marker", "-fmodules-validate-once-per-build-session",
"-MMD", "-fno-delete-null-pointer-checks",
"-MP", "-fno-use-linker-plugin"
"-MQ", "-fno-var-tracking",
"-MT", "-fno-var-tracking-assignments", "-fno-enforce-eh-specs", "-fvar-tracking",
"-Og", "-fvar-tracking-assignments", "-fvar-tracking-assignments-toggle",
"-Wa,--32", "-gcc-toolchain",
"-Wa,--64", "-march=", "-masm=", "-mcpu=", "-mfpmath=", "-mtune=", "-s",
"-Wl,--incremental-full",
"-Wl,--incremental-patch,1",
"-Wl,--no-incremental",
"-fbuild-session-file=",
"-fbuild-session-timestamp=",
"-fembed-bitcode",
"-fembed-bitcode-marker",
"-fmodules-validate-once-per-build-session",
"-fno-delete-null-pointer-checks",
"-fno-use-linker-plugin"
"-fno-var-tracking",
"-fno-var-tracking-assignments",
"-fno-enforce-eh-specs",
"-fvar-tracking",
"-fvar-tracking-assignments",
"-fvar-tracking-assignments-toggle",
"-gcc-toolchain",
"-march=",
"-masm=",
"-mcpu=",
"-mfpmath=",
"-mtune=",
"-s",
"-B", "-B",
//"-f", //"-f",
//"-pipe", //"-pipe",
//"-W", //"-W",
// TODO: make sure we consume includes before stripping all path-like args. // TODO: make sure we consume includes before stripping all path-like args.
"/work/goma/gomacc", "/work/goma/gomacc",
"../../third_party/llvm-build/Release+Asserts/bin/clang++", "../../third_party/llvm-build/Release+Asserts/bin/clang++",
"-Wno-unused-lambda-capture", "-Wno-unused-lambda-capture", "/", "..",
"/", //"-stdlib=libc++"
"..",
//"-stdlib=libc++"
}; };
// Arguments which are followed by a potentially relative path. We need to make // Arguments which are followed by a potentially relative path. We need to make
// all relative paths absolute, otherwise libclang will not resolve them. // all relative paths absolute, otherwise libclang will not resolve them.
const char* kPathArgs[] = { const char* kPathArgs[] = {"-I", "-iquote", "-isystem", "--sysroot="};
"-I",
"-iquote",
"-isystem",
"--sysroot="
};
const char* kQuoteIncludeArgs[] = { const char* kQuoteIncludeArgs[] = {"-iquote"};
"-iquote" const char* kAngleIncludeArgs[] = {"-I", "-isystem"};
};
const char* kAngleIncludeArgs[] = {
"-I",
"-isystem"
};
bool ShouldAddToQuoteIncludes(const std::string& arg) { bool ShouldAddToQuoteIncludes(const std::string& arg) {
for (const char* flag_type : kQuoteIncludeArgs) { for (const char* flag_type : kQuoteIncludeArgs) {
@ -122,8 +84,10 @@ bool IsCFile(const std::string& path) {
} }
Project::Entry GetCompilationEntryFromCompileCommandEntry( Project::Entry GetCompilationEntryFromCompileCommandEntry(
std::unordered_set<std::string>& quote_includes, std::unordered_set<std::string>& angle_includes, std::unordered_set<std::string>& quote_includes,
const std::vector<std::string>& extra_flags, const CompileCommandsEntry& entry) { std::unordered_set<std::string>& angle_includes,
const std::vector<std::string>& extra_flags,
const CompileCommandsEntry& entry) {
Project::Entry result; Project::Entry result;
result.filename = NormalizePath(entry.file); result.filename = NormalizePath(entry.file);
@ -136,15 +100,15 @@ Project::Entry GetCompilationEntryFromCompileCommandEntry(
std::string arg = entry.args[i]; std::string arg = entry.args[i];
// If blacklist skip. // If blacklist skip.
if (std::any_of(std::begin(kBlacklistMulti), std::end(kBlacklistMulti), [&arg](const char* value) { if (std::any_of(
return StartsWith(arg, value); std::begin(kBlacklistMulti), std::end(kBlacklistMulti),
})) { [&arg](const char* value) { return StartsWith(arg, value); })) {
++i; ++i;
continue; continue;
} }
if (std::any_of(std::begin(kBlacklist), std::end(kBlacklist), [&arg](const char* value) { if (std::any_of(
return StartsWith(arg, value); std::begin(kBlacklist), std::end(kBlacklist),
})) { [&arg](const char* value) { return StartsWith(arg, value); })) {
continue; continue;
} }
@ -153,7 +117,7 @@ Project::Entry GetCompilationEntryFromCompileCommandEntry(
if (arg.size() > 0 && arg[0] != '/') if (arg.size() > 0 && arg[0] != '/')
arg = NormalizePath(entry.directory + arg); arg = NormalizePath(entry.directory + arg);
make_next_flag_absolute = false; make_next_flag_absolute = false;
if (add_next_flag_quote) if (add_next_flag_quote)
quote_includes.insert(arg); quote_includes.insert(arg);
if (add_next_flag_angle) if (add_next_flag_angle)
@ -215,12 +179,12 @@ Project::Entry GetCompilationEntryFromCompileCommandEntry(
/* TODO: Fix this function, it may be way faster than libclang's implementation. /* TODO: Fix this function, it may be way faster than libclang's implementation.
std::vector<Project::Entry> LoadFromCompileCommandsJson( std::vector<Project::Entry> LoadFromCompileCommandsJson(
std::unordered_set<std::string>& quote_includes, std::unordered_set<std::string>& angle_includes, std::unordered_set<std::string>& quote_includes,
const std::vector<std::string>& extra_flags, const std::string& project_directory) { std::unordered_set<std::string>& angle_includes, const std::vector<std::string>&
extra_flags, const std::string& project_directory) {
optional<std::string> compile_commands_content = ReadContent(project_directory + "/compile_commands.json"); optional<std::string> compile_commands_content = ReadContent(project_directory
if (!compile_commands_content) + "/compile_commands.json"); if (!compile_commands_content) return {};
return {};
rapidjson::Document reader; rapidjson::Document reader;
reader.Parse(compile_commands_content->c_str()); reader.Parse(compile_commands_content->c_str());
@ -236,15 +200,18 @@ std::vector<Project::Entry> LoadFromCompileCommandsJson(
if (entry.args.empty() && !entry.command.empty()) if (entry.args.empty() && !entry.command.empty())
entry.args = SplitString(entry.command, " "); entry.args = SplitString(entry.command, " ");
result.push_back(GetCompilationEntryFromCompileCommandEntry(quote_includes, angle_includes, extra_flags, entry)); result.push_back(GetCompilationEntryFromCompileCommandEntry(quote_includes,
angle_includes, extra_flags, entry));
} }
return result; return result;
} }
*/ */
std::vector<Project::Entry> LoadFromDirectoryListing( std::vector<Project::Entry> LoadFromDirectoryListing(
std::unordered_set<std::string>& quote_includes, std::unordered_set<std::string>& angle_includes, std::unordered_set<std::string>& quote_includes,
const std::vector<std::string>& extra_flags, const std::string& project_directory) { std::unordered_set<std::string>& angle_includes,
const std::vector<std::string>& extra_flags,
const std::string& project_directory) {
std::vector<Project::Entry> result; std::vector<Project::Entry> result;
std::vector<std::string> args; std::vector<std::string> args;
@ -259,14 +226,16 @@ std::vector<Project::Entry> LoadFromDirectoryListing(
} }
std::cerr << std::endl; std::cerr << std::endl;
std::vector<std::string> files = GetFilesInFolder(
std::vector<std::string> files = GetFilesInFolder(project_directory, true /*recursive*/, true /*add_folder_to_path*/); project_directory, true /*recursive*/, true /*add_folder_to_path*/);
for (const std::string& file : files) { for (const std::string& file : files) {
if (EndsWith(file, ".cc") || EndsWith(file, ".cpp") || EndsWith(file, ".c")) { if (EndsWith(file, ".cc") || EndsWith(file, ".cpp") ||
EndsWith(file, ".c")) {
CompileCommandsEntry e; CompileCommandsEntry e;
e.file = NormalizePath(file); e.file = NormalizePath(file);
e.args = args; e.args = args;
result.push_back(GetCompilationEntryFromCompileCommandEntry(quote_includes, angle_includes, extra_flags, e)); result.push_back(GetCompilationEntryFromCompileCommandEntry(
quote_includes, angle_includes, extra_flags, e));
} }
} }
@ -274,28 +243,38 @@ std::vector<Project::Entry> LoadFromDirectoryListing(
} }
std::vector<Project::Entry> LoadCompilationEntriesFromDirectory( std::vector<Project::Entry> LoadCompilationEntriesFromDirectory(
std::unordered_set<std::string>& quote_includes, std::unordered_set<std::string>& angle_includes, std::unordered_set<std::string>& quote_includes,
const std::vector<std::string>& extra_flags, const std::string& project_directory) { std::unordered_set<std::string>& angle_includes,
const std::vector<std::string>& extra_flags,
const std::string& project_directory) {
// TODO: Figure out if this function or the clang one is faster. // TODO: Figure out if this function or the clang one is faster.
//return LoadFromCompileCommandsJson(extra_flags, project_directory); // return LoadFromCompileCommandsJson(extra_flags, project_directory);
std::cerr << "Trying to load compile_commands.json" << std::endl; std::cerr << "Trying to load compile_commands.json" << std::endl;
CXCompilationDatabase_Error cx_db_load_error; CXCompilationDatabase_Error cx_db_load_error;
CXCompilationDatabase cx_db = clang_CompilationDatabase_fromDirectory(project_directory.c_str(), &cx_db_load_error); CXCompilationDatabase cx_db = clang_CompilationDatabase_fromDirectory(
project_directory.c_str(), &cx_db_load_error);
if (cx_db_load_error == CXCompilationDatabase_CanNotLoadDatabase) { if (cx_db_load_error == CXCompilationDatabase_CanNotLoadDatabase) {
std::cerr << "Unable to load compile_commands.json located at \"" << project_directory << "\"; using directory listing instead." << std::endl; std::cerr << "Unable to load compile_commands.json located at \""
return LoadFromDirectoryListing(quote_includes, angle_includes, extra_flags, project_directory); << project_directory << "\"; using directory listing instead."
<< std::endl;
return LoadFromDirectoryListing(quote_includes, angle_includes, extra_flags,
project_directory);
} }
CXCompileCommands cx_commands = clang_CompilationDatabase_getAllCompileCommands(cx_db); CXCompileCommands cx_commands =
clang_CompilationDatabase_getAllCompileCommands(cx_db);
unsigned int num_commands = clang_CompileCommands_getSize(cx_commands); unsigned int num_commands = clang_CompileCommands_getSize(cx_commands);
std::vector<Project::Entry> result; std::vector<Project::Entry> result;
for (unsigned int i = 0; i < num_commands; i++) { for (unsigned int i = 0; i < num_commands; i++) {
CXCompileCommand cx_command = clang_CompileCommands_getCommand(cx_commands, i); CXCompileCommand cx_command =
clang_CompileCommands_getCommand(cx_commands, i);
std::string directory = clang::ToString(clang_CompileCommand_getDirectory(cx_command)); std::string directory =
std::string relative_filename = clang::ToString(clang_CompileCommand_getFilename(cx_command)); clang::ToString(clang_CompileCommand_getDirectory(cx_command));
std::string relative_filename =
clang::ToString(clang_CompileCommand_getFilename(cx_command));
std::string absolute_filename = directory + "/" + relative_filename; std::string absolute_filename = directory + "/" + relative_filename;
CompileCommandsEntry entry; CompileCommandsEntry entry;
@ -305,9 +284,11 @@ std::vector<Project::Entry> LoadCompilationEntriesFromDirectory(
unsigned num_args = clang_CompileCommand_getNumArgs(cx_command); unsigned num_args = clang_CompileCommand_getNumArgs(cx_command);
entry.args.reserve(num_args); entry.args.reserve(num_args);
for (unsigned j = 0; j < num_args; ++j) for (unsigned j = 0; j < num_args; ++j)
entry.args.push_back(clang::ToString(clang_CompileCommand_getArg(cx_command, j))); entry.args.push_back(
clang::ToString(clang_CompileCommand_getArg(cx_command, j)));
result.push_back(GetCompilationEntryFromCompileCommandEntry(quote_includes, angle_includes, extra_flags, entry)); result.push_back(GetCompilationEntryFromCompileCommandEntry(
quote_includes, angle_includes, extra_flags, entry));
} }
clang_CompileCommands_dispose(cx_commands); clang_CompileCommands_dispose(cx_commands);
@ -356,14 +337,18 @@ int ComputeGuessScore(const std::string& a, const std::string& b) {
} // namespace } // namespace
void Project::Load(const std::vector<std::string>& extra_flags, const std::string& directory) { void Project::Load(const std::vector<std::string>& extra_flags,
const std::string& directory) {
std::unordered_set<std::string> unique_quote_includes; std::unordered_set<std::string> unique_quote_includes;
std::unordered_set<std::string> unique_angle_includes; std::unordered_set<std::string> unique_angle_includes;
entries = LoadCompilationEntriesFromDirectory(unique_quote_includes, unique_angle_includes, extra_flags, directory); entries = LoadCompilationEntriesFromDirectory(
unique_quote_includes, unique_angle_includes, extra_flags, directory);
quote_include_directories.assign(unique_quote_includes.begin(), unique_quote_includes.end()); quote_include_directories.assign(unique_quote_includes.begin(),
angle_include_directories.assign(unique_angle_includes.begin(), unique_angle_includes.end()); unique_quote_includes.end());
angle_include_directories.assign(unique_angle_includes.begin(),
unique_angle_includes.end());
for (std::string& path : quote_include_directories) { for (std::string& path : quote_include_directories) {
EnsureEndsInSlash(path); EnsureEndsInSlash(path);
@ -379,7 +364,8 @@ void Project::Load(const std::vector<std::string>& extra_flags, const std::strin
absolute_path_to_entry_index_[entries[i].filename] = i; absolute_path_to_entry_index_[entries[i].filename] = i;
} }
Project::Entry Project::FindCompilationEntryForFile(const std::string& filename) { Project::Entry Project::FindCompilationEntryForFile(
const std::string& filename) {
auto it = absolute_path_to_entry_index_.find(filename); auto it = absolute_path_to_entry_index_.find(filename);
if (it != absolute_path_to_entry_index_.end()) if (it != absolute_path_to_entry_index_.end())
return entries[it->second]; return entries[it->second];
@ -404,7 +390,9 @@ Project::Entry Project::FindCompilationEntryForFile(const std::string& filename)
return result; return result;
} }
void Project::ForAllFilteredFiles(Config* config, std::function<void(int i, const Entry& entry)> action) { void Project::ForAllFilteredFiles(
Config* config,
std::function<void(int i, const Entry& entry)> action) {
GroupMatch matcher(config->indexWhitelist, config->indexBlacklist); GroupMatch matcher(config->indexWhitelist, config->indexBlacklist);
for (int i = 0; i < entries.size(); ++i) { for (int i = 0; i < entries.size(); ++i) {
const Project::Entry& entry = entries[i]; const Project::Entry& entry = entries[i];
@ -413,7 +401,8 @@ void Project::ForAllFilteredFiles(Config* config, std::function<void(int i, cons
action(i, entries[i]); action(i, entries[i]);
else { else {
if (config->logSkippedPathsForIndex) { if (config->logSkippedPathsForIndex) {
LOG_S(INFO) << "[" << i + 1 << "/" << entries.size() << "]: Failed " << failure_reason << "; skipping " << entry.filename; LOG_S(INFO) << "[" << i + 1 << "/" << entries.size() << "]: Failed "
<< failure_reason << "; skipping " << entry.filename;
} }
} }
} }
@ -425,36 +414,39 @@ TEST_CASE("Entry inference") {
Project p; Project p;
{ {
Project::Entry e; Project::Entry e;
e.args = { "arg1" }; e.args = {"arg1"};
e.filename = "/a/b/c/d/bar.cc"; e.filename = "/a/b/c/d/bar.cc";
p.entries.push_back(e); p.entries.push_back(e);
} }
{ {
Project::Entry e; Project::Entry e;
e.args = { "arg2" }; e.args = {"arg2"};
e.filename = "/a/b/c/baz.cc"; e.filename = "/a/b/c/baz.cc";
p.entries.push_back(e); p.entries.push_back(e);
} }
// Guess at same directory level, when there are parent directories. // Guess at same directory level, when there are parent directories.
{ {
optional<Project::Entry> entry = p.FindCompilationEntryForFile("/a/b/c/d/new.cc"); optional<Project::Entry> entry =
p.FindCompilationEntryForFile("/a/b/c/d/new.cc");
REQUIRE(entry.has_value()); REQUIRE(entry.has_value());
REQUIRE(entry->args == std::vector<std::string>{ "arg1" }); REQUIRE(entry->args == std::vector<std::string>{"arg1"});
} }
// Guess at same directory level, when there are child directories. // Guess at same directory level, when there are child directories.
{ {
optional<Project::Entry> entry = p.FindCompilationEntryForFile("/a/b/c/new.cc"); optional<Project::Entry> entry =
p.FindCompilationEntryForFile("/a/b/c/new.cc");
REQUIRE(entry.has_value()); REQUIRE(entry.has_value());
REQUIRE(entry->args == std::vector<std::string>{ "arg2" }); REQUIRE(entry->args == std::vector<std::string>{"arg2"});
} }
// Guess at new directory (use the closest parent directory). // Guess at new directory (use the closest parent directory).
{ {
optional<Project::Entry> entry = p.FindCompilationEntryForFile("/a/b/c/new/new.cc"); optional<Project::Entry> entry =
p.FindCompilationEntryForFile("/a/b/c/new/new.cc");
REQUIRE(entry.has_value()); REQUIRE(entry.has_value());
REQUIRE(entry->args == std::vector<std::string>{ "arg2" }); REQUIRE(entry->args == std::vector<std::string>{"arg2"});
} }
} }
@ -462,51 +454,55 @@ TEST_CASE("Entry inference prefers same file endings") {
Project p; Project p;
{ {
Project::Entry e; Project::Entry e;
e.args = { "arg1" }; e.args = {"arg1"};
e.filename = "common/simple_browsertest.cc"; e.filename = "common/simple_browsertest.cc";
p.entries.push_back(e); p.entries.push_back(e);
} }
{ {
Project::Entry e; Project::Entry e;
e.args = { "arg2" }; e.args = {"arg2"};
e.filename = "common/simple_unittest.cc"; e.filename = "common/simple_unittest.cc";
p.entries.push_back(e); p.entries.push_back(e);
} }
{ {
Project::Entry e; Project::Entry e;
e.args = { "arg3" }; e.args = {"arg3"};
e.filename = "common/a/simple_unittest.cc"; e.filename = "common/a/simple_unittest.cc";
p.entries.push_back(e); p.entries.push_back(e);
} }
// Prefer files with the same ending. // Prefer files with the same ending.
{ {
optional<Project::Entry> entry = p.FindCompilationEntryForFile("my_browsertest.cc"); optional<Project::Entry> entry =
p.FindCompilationEntryForFile("my_browsertest.cc");
REQUIRE(entry.has_value()); REQUIRE(entry.has_value());
REQUIRE(entry->args == std::vector<std::string>{ "arg1" }); REQUIRE(entry->args == std::vector<std::string>{"arg1"});
} }
{ {
optional<Project::Entry> entry = p.FindCompilationEntryForFile("my_unittest.cc"); optional<Project::Entry> entry =
p.FindCompilationEntryForFile("my_unittest.cc");
REQUIRE(entry.has_value()); REQUIRE(entry.has_value());
REQUIRE(entry->args == std::vector<std::string>{ "arg2" }); REQUIRE(entry->args == std::vector<std::string>{"arg2"});
} }
{ {
optional<Project::Entry> entry = p.FindCompilationEntryForFile("common/my_browsertest.cc"); optional<Project::Entry> entry =
p.FindCompilationEntryForFile("common/my_browsertest.cc");
REQUIRE(entry.has_value()); REQUIRE(entry.has_value());
REQUIRE(entry->args == std::vector<std::string>{ "arg1" }); REQUIRE(entry->args == std::vector<std::string>{"arg1"});
} }
{ {
optional<Project::Entry> entry = p.FindCompilationEntryForFile("common/my_unittest.cc"); optional<Project::Entry> entry =
p.FindCompilationEntryForFile("common/my_unittest.cc");
REQUIRE(entry.has_value()); REQUIRE(entry.has_value());
REQUIRE(entry->args == std::vector<std::string>{ "arg2" }); REQUIRE(entry->args == std::vector<std::string>{"arg2"});
} }
// Prefer the same directory over matching file-ending. // Prefer the same directory over matching file-ending.
{ {
optional<Project::Entry> entry = p.FindCompilationEntryForFile("common/a/foo.cc"); optional<Project::Entry> entry =
p.FindCompilationEntryForFile("common/a/foo.cc");
REQUIRE(entry.has_value()); REQUIRE(entry.has_value());
REQUIRE(entry->args == std::vector<std::string>{ "arg3" }); REQUIRE(entry->args == std::vector<std::string>{"arg3"});
} }
} }

View File

@ -10,8 +10,8 @@
#include <string> #include <string>
#include <vector> #include <vector>
using std::experimental::optional;
using std::experimental::nullopt; using std::experimental::nullopt;
using std::experimental::optional;
struct Project { struct Project {
struct Entry { struct Entry {
@ -35,12 +35,14 @@ struct Project {
// discover all files and args. Otherwise, a recursive directory listing of // discover all files and args. Otherwise, a recursive directory listing of
// all *.cpp, *.cc, *.h, and *.hpp files will be used. clang arguments can be // all *.cpp, *.cc, *.h, and *.hpp files will be used. clang arguments can be
// specified in a clang_args file located inside of |directory|. // specified in a clang_args file located inside of |directory|.
void Load(const std::vector<std::string>& extra_flags, const std::string& directory); void Load(const std::vector<std::string>& extra_flags,
const std::string& directory);
// Lookup the CompilationEntry for |filename|. If no entry was found this // Lookup the CompilationEntry for |filename|. If no entry was found this
// will infer one based on existing project structure. // will infer one based on existing project structure.
Entry FindCompilationEntryForFile(const std::string& filename); Entry FindCompilationEntryForFile(const std::string& filename);
void ForAllFilteredFiles(Config* config, std::function<void(int i, const Entry& entry)> action); void ForAllFilteredFiles(
Config* config,
std::function<void(int i, const Entry& entry)> action);
}; };

View File

@ -2,24 +2,26 @@
#include "indexer.h" #include "indexer.h"
#include <optional.h>
#include <doctest/doctest.h> #include <doctest/doctest.h>
#include <optional.h>
#include <loguru.hpp> #include <loguru.hpp>
#include <cassert> #include <cassert>
#include <cstdint> #include <cstdint>
#include <functional> #include <functional>
#include <unordered_set>
#include <unordered_map>
#include <string>
#include <iostream> #include <iostream>
#include <string>
#include <unordered_map>
#include <unordered_set>
// TODO: Make all copy constructors explicit. // TODO: Make all copy constructors explicit.
namespace { namespace {
optional<QueryType::DefUpdate> ToQuery(const IdMap& id_map, const IndexType::Def& type) { optional<QueryType::DefUpdate> ToQuery(const IdMap& id_map,
const IndexType::Def& type) {
if (type.detailed_name.empty()) if (type.detailed_name.empty())
return nullopt; return nullopt;
@ -36,7 +38,8 @@ optional<QueryType::DefUpdate> ToQuery(const IdMap& id_map, const IndexType::Def
return result; return result;
} }
optional<QueryFunc::DefUpdate> ToQuery(const IdMap& id_map, const IndexFunc::Def& func) { optional<QueryFunc::DefUpdate> ToQuery(const IdMap& id_map,
const IndexFunc::Def& func) {
if (func.detailed_name.empty()) if (func.detailed_name.empty())
return nullopt; return nullopt;
@ -52,7 +55,8 @@ optional<QueryFunc::DefUpdate> ToQuery(const IdMap& id_map, const IndexFunc::Def
return result; return result;
} }
optional<QueryVar::DefUpdate> ToQuery(const IdMap& id_map, const IndexVar::Def& var) { optional<QueryVar::DefUpdate> ToQuery(const IdMap& id_map,
const IndexVar::Def& var) {
if (var.detailed_name.empty()) if (var.detailed_name.empty())
return nullopt; return nullopt;
@ -69,15 +73,13 @@ optional<QueryVar::DefUpdate> ToQuery(const IdMap& id_map, const IndexVar::Def&
return result; return result;
} }
// Adds the mergeable updates in |source| to |dest|. If a mergeable update for // Adds the mergeable updates in |source| to |dest|. If a mergeable update for
// the destination type already exists, it will be combined. This makes merging // the destination type already exists, it will be combined. This makes merging
// updates take longer but reduces import time on the querydb thread. // updates take longer but reduces import time on the querydb thread.
template <typename TId, typename TValue> template <typename TId, typename TValue>
void AddMergeableRange( void AddMergeableRange(
std::vector<MergeableUpdate<TId, TValue>>* dest, std::vector<MergeableUpdate<TId, TValue>>* dest,
const std::vector<MergeableUpdate<TId, TValue>>& source) { const std::vector<MergeableUpdate<TId, TValue>>& source) {
// TODO: Consider caching the lookup table. It can probably save even more // TODO: Consider caching the lookup table. It can probably save even more
// time at the cost of some additional memory. // time at the cost of some additional memory.
@ -93,8 +95,7 @@ void AddMergeableRange(
if (it != id_to_index.end()) { if (it != id_to_index.end()) {
AddRange(&(*dest)[it->second].to_add, entry.to_add); AddRange(&(*dest)[it->second].to_add, entry.to_add);
AddRange(&(*dest)[it->second].to_remove, entry.to_remove); AddRange(&(*dest)[it->second].to_remove, entry.to_remove);
} } else {
else {
dest->push_back(entry); dest->push_back(entry);
} }
} }
@ -105,34 +106,31 @@ void AddMergeableRange(
// that are in |current| but not |previous| to |added|. // that are in |current| but not |previous| to |added|.
// //
// Returns true iff |removed| or |added| are non-empty. // Returns true iff |removed| or |added| are non-empty.
template<typename T> template <typename T>
bool ComputeDifferenceForUpdate( bool ComputeDifferenceForUpdate(std::vector<T>& previous,
std::vector<T>& previous, std::vector<T>& current, std::vector<T>& current,
std::vector<T>* removed, std::vector<T>* added) { std::vector<T>* removed,
std::vector<T>* added) {
// We need to sort to use std::set_difference. // We need to sort to use std::set_difference.
std::sort(previous.begin(), previous.end()); std::sort(previous.begin(), previous.end());
std::sort(current.begin(), current.end()); std::sort(current.begin(), current.end());
// Returns the elements in |previous| that are not in |current|. // Returns the elements in |previous| that are not in |current|.
std::set_difference( std::set_difference(previous.begin(), previous.end(), current.begin(),
previous.begin(), previous.end(), current.end(), std::back_inserter(*removed));
current.begin(), current.end(),
std::back_inserter(*removed));
// Returns the elements in |current| that are not in |previous|. // Returns the elements in |current| that are not in |previous|.
std::set_difference( std::set_difference(current.begin(), current.end(), previous.begin(),
current.begin(), current.end(), previous.end(), std::back_inserter(*added));
previous.begin(), previous.end(),
std::back_inserter(*added));
return !removed->empty() || !added->empty(); return !removed->empty() || !added->empty();
} }
template<typename T> template <typename T>
void CompareGroups( void CompareGroups(std::vector<T>& previous_data,
std::vector<T>& previous_data, std::vector<T>& current_data, std::vector<T>& current_data,
std::function<void(T*)> on_removed, std::function<void(T*)> on_added, std::function<void(T*, T*)> on_found) { std::function<void(T*)> on_removed,
std::function<void(T*)> on_added,
std::function<void(T*, T*)> on_found) {
std::sort(previous_data.begin(), previous_data.end()); std::sort(previous_data.begin(), previous_data.end());
std::sort(current_data.begin(), current_data.end()); std::sort(current_data.begin(), current_data.end());
@ -186,7 +184,8 @@ QueryFile::Def BuildFileDef(const IdMap& id_map, const IndexFile& indexed) {
for (const IndexType& type : indexed.types) { for (const IndexType& type : indexed.types) {
if (type.def.definition_spelling.has_value()) if (type.def.definition_spelling.has_value())
add_all_symbols(id_map.ToSymbol(type.id), type.def.definition_spelling.value()); add_all_symbols(id_map.ToSymbol(type.id),
type.def.definition_spelling.value());
if (type.def.definition_extent.has_value()) if (type.def.definition_extent.has_value())
add_outline(id_map.ToSymbol(type.id), type.def.definition_extent.value()); add_outline(id_map.ToSymbol(type.id), type.def.definition_extent.value());
for (const Range& use : type.uses) for (const Range& use : type.uses)
@ -194,7 +193,8 @@ QueryFile::Def BuildFileDef(const IdMap& id_map, const IndexFile& indexed) {
} }
for (const IndexFunc& func : indexed.funcs) { for (const IndexFunc& func : indexed.funcs) {
if (func.def.definition_spelling.has_value()) if (func.def.definition_spelling.has_value())
add_all_symbols(id_map.ToSymbol(func.id), func.def.definition_spelling.value()); add_all_symbols(id_map.ToSymbol(func.id),
func.def.definition_spelling.value());
if (func.def.definition_extent.has_value()) if (func.def.definition_extent.has_value())
add_outline(id_map.ToSymbol(func.id), func.def.definition_extent.value()); add_outline(id_map.ToSymbol(func.id), func.def.definition_extent.value());
for (const IndexFunc::Declaration& decl : func.declarations) { for (const IndexFunc::Declaration& decl : func.declarations) {
@ -203,63 +203,37 @@ QueryFile::Def BuildFileDef(const IdMap& id_map, const IndexFile& indexed) {
add_outline(id_map.ToSymbol(func.id), decl.spelling); add_outline(id_map.ToSymbol(func.id), decl.spelling);
} }
for (const IndexFuncRef& caller : func.callers) { for (const IndexFuncRef& caller : func.callers) {
if (caller.is_implicit) continue; if (caller.is_implicit)
continue;
add_all_symbols(id_map.ToSymbol(func.id), caller.loc); add_all_symbols(id_map.ToSymbol(func.id), caller.loc);
} }
} }
for (const IndexVar& var : indexed.vars) { for (const IndexVar& var : indexed.vars) {
if (var.def.definition_spelling.has_value()) if (var.def.definition_spelling.has_value())
add_all_symbols(id_map.ToSymbol(var.id), var.def.definition_spelling.value()); add_all_symbols(id_map.ToSymbol(var.id),
var.def.definition_spelling.value());
if (var.def.definition_extent.has_value()) if (var.def.definition_extent.has_value())
add_outline(id_map.ToSymbol(var.id), var.def.definition_extent.value()); add_outline(id_map.ToSymbol(var.id), var.def.definition_extent.value());
for (const Range& use : var.uses) for (const Range& use : var.uses)
add_all_symbols(id_map.ToSymbol(var.id), use); add_all_symbols(id_map.ToSymbol(var.id), use);
} }
std::sort(def.outline.begin(), def.outline.end(), [](const SymbolRef& a, const SymbolRef& b) { std::sort(def.outline.begin(), def.outline.end(),
return a.loc.range.start < b.loc.range.start; [](const SymbolRef& a, const SymbolRef& b) {
}); return a.loc.range.start < b.loc.range.start;
std::sort(def.all_symbols.begin(), def.all_symbols.end(), [](const SymbolRef& a, const SymbolRef& b) { });
return a.loc.range.start < b.loc.range.start; std::sort(def.all_symbols.begin(), def.all_symbols.end(),
}); [](const SymbolRef& a, const SymbolRef& b) {
return a.loc.range.start < b.loc.range.start;
});
return def; return def;
} }
} // namespace } // namespace
QueryFileId GetQueryFileIdFromPath(QueryDatabase* query_db,
const std::string& path) {
QueryFileId GetQueryFileIdFromPath(QueryDatabase* query_db, const std::string& path) {
auto it = query_db->usr_to_file.find(LowerPathIfCaseInsensitive(path)); auto it = query_db->usr_to_file.find(LowerPathIfCaseInsensitive(path));
if (it != query_db->usr_to_file.end()) if (it != query_db->usr_to_file.end())
return QueryFileId(it->second.id); return QueryFileId(it->second.id);
@ -304,17 +278,19 @@ QueryVarId GetQueryVarIdFromUsr(QueryDatabase* query_db, const Usr& usr) {
} }
IdMap::IdMap(QueryDatabase* query_db, const IdCache& local_ids) IdMap::IdMap(QueryDatabase* query_db, const IdCache& local_ids)
: local_ids(local_ids) { : local_ids(local_ids) {
//LOG_S(INFO) << "Creating IdMap for " << local_ids.primary_file; // LOG_S(INFO) << "Creating IdMap for " << local_ids.primary_file;
primary_file = GetQueryFileIdFromPath(query_db, local_ids.primary_file); primary_file = GetQueryFileIdFromPath(query_db, local_ids.primary_file);
cached_type_ids_.resize(local_ids.type_id_to_usr.size()); cached_type_ids_.resize(local_ids.type_id_to_usr.size());
for (const auto& entry : local_ids.type_id_to_usr) for (const auto& entry : local_ids.type_id_to_usr)
cached_type_ids_[entry.first] = GetQueryTypeIdFromUsr(query_db, entry.second); cached_type_ids_[entry.first] =
GetQueryTypeIdFromUsr(query_db, entry.second);
cached_func_ids_.resize(local_ids.func_id_to_usr.size()); cached_func_ids_.resize(local_ids.func_id_to_usr.size());
for (const auto& entry : local_ids.func_id_to_usr) for (const auto& entry : local_ids.func_id_to_usr)
cached_func_ids_[entry.first] = GetQueryFuncIdFromUsr(query_db, entry.second); cached_func_ids_[entry.first] =
GetQueryFuncIdFromUsr(query_db, entry.second);
cached_var_ids_.resize(local_ids.var_id_to_usr.size()); cached_var_ids_.resize(local_ids.var_id_to_usr.size());
for (const auto& entry : local_ids.var_id_to_usr) for (const auto& entry : local_ids.var_id_to_usr)
@ -329,7 +305,8 @@ QueryTypeId IdMap::ToQuery(IndexTypeId id) const {
return QueryTypeId(cached_type_ids_.find(id)->second); return QueryTypeId(cached_type_ids_.find(id)->second);
} }
QueryFuncId IdMap::ToQuery(IndexFuncId id) const { QueryFuncId IdMap::ToQuery(IndexFuncId id) const {
if (id.id == -1) return QueryFuncId((size_t)-1); if (id.id == -1)
return QueryFuncId((size_t)-1);
assert(cached_func_ids_.find(id) != cached_func_ids_.end()); assert(cached_func_ids_.find(id) != cached_func_ids_.end());
return QueryFuncId(cached_func_ids_.find(id)->second); return QueryFuncId(cached_func_ids_.find(id)->second);
} }
@ -370,14 +347,16 @@ optional<QueryFuncRef> IdMap::ToQuery(optional<IndexFuncRef> ref) const {
return nullopt; return nullopt;
return ToQuery(ref.value()); return ToQuery(ref.value());
} }
optional<QueryLocation> IdMap::ToQuery(optional<IndexFunc::Declaration> decl) const { optional<QueryLocation> IdMap::ToQuery(
optional<IndexFunc::Declaration> decl) const {
if (!decl) if (!decl)
return nullopt; return nullopt;
return ToQuery(decl.value()); return ToQuery(decl.value());
} }
template<typename In, typename Out> template <typename In, typename Out>
std::vector<Out> ToQueryTransform(const IdMap& id_map, const std::vector<In>& input) { std::vector<Out> ToQueryTransform(const IdMap& id_map,
const std::vector<In>& input) {
std::vector<Out> result; std::vector<Out> result;
result.reserve(input.size()); result.reserve(input.size());
for (const In& in : input) for (const In& in : input)
@ -399,7 +378,8 @@ std::vector<QueryVarId> IdMap::ToQuery(std::vector<IndexVarId> ids) const {
std::vector<QueryFuncRef> IdMap::ToQuery(std::vector<IndexFuncRef> refs) const { std::vector<QueryFuncRef> IdMap::ToQuery(std::vector<IndexFuncRef> refs) const {
return ToQueryTransform<IndexFuncRef, QueryFuncRef>(*this, refs); return ToQueryTransform<IndexFuncRef, QueryFuncRef>(*this, refs);
} }
std::vector<QueryLocation> IdMap::ToQuery(std::vector<IndexFunc::Declaration> decls) const { std::vector<QueryLocation> IdMap::ToQuery(
std::vector<IndexFunc::Declaration> decls) const {
return ToQueryTransform<IndexFunc::Declaration, QueryLocation>(*this, decls); return ToQueryTransform<IndexFunc::Declaration, QueryLocation>(*this, decls);
} }
@ -412,33 +392,16 @@ SymbolIdx IdMap::ToSymbol(IndexFuncId id) const {
SymbolIdx IdMap::ToSymbol(IndexVarId id) const { SymbolIdx IdMap::ToSymbol(IndexVarId id) const {
return SymbolIdx(SymbolKind::Var, ToQuery(id).id); return SymbolIdx(SymbolKind::Var, ToQuery(id).id);
} }
// ---------------------- // ----------------------
// INDEX THREAD FUNCTIONS // INDEX THREAD FUNCTIONS
// ---------------------- // ----------------------
// static // static
IndexUpdate IndexUpdate::CreateDelta(const IdMap* previous_id_map, const IdMap* current_id_map, IndexFile* previous, IndexFile* current) { IndexUpdate IndexUpdate::CreateDelta(const IdMap* previous_id_map,
const IdMap* current_id_map,
IndexFile* previous,
IndexFile* current) {
// This function runs on an indexer thread. // This function runs on an indexer thread.
if (!previous_id_map) { if (!previous_id_map) {
@ -449,25 +412,30 @@ IndexUpdate IndexUpdate::CreateDelta(const IdMap* previous_id_map, const IdMap*
return IndexUpdate(*previous_id_map, *current_id_map, *previous, *current); return IndexUpdate(*previous_id_map, *current_id_map, *previous, *current);
} }
IndexUpdate::IndexUpdate(const IdMap& previous_id_map, const IdMap& current_id_map, IndexFile& previous_file, IndexFile& current_file) { IndexUpdate::IndexUpdate(const IdMap& previous_id_map,
// This function runs on an indexer thread. const IdMap& current_id_map,
IndexFile& previous_file,
IndexFile& current_file) {
// This function runs on an indexer thread.
// |query_name| is the name of the variable on the query type. // |query_name| is the name of the variable on the query type.
// |index_name| is the name of the variable on the index type. // |index_name| is the name of the variable on the index type.
// |type| is the type of the variable. // |type| is the type of the variable.
#define PROCESS_UPDATE_DIFF(type_id, query_name, index_name, type) \ #define PROCESS_UPDATE_DIFF(type_id, query_name, index_name, type) \
{ \ { \
/* Check for changes. */ \ /* Check for changes. */ \
std::vector<type> removed, added; \ std::vector<type> removed, added; \
auto previous = previous_id_map.ToQuery(previous_def->index_name); \ auto previous = previous_id_map.ToQuery(previous_def->index_name); \
auto current = current_id_map.ToQuery(current_def->index_name); \ auto current = current_id_map.ToQuery(current_def->index_name); \
bool did_add = ComputeDifferenceForUpdate( \ bool did_add = \
previous, current, \ ComputeDifferenceForUpdate(previous, current, &removed, &added); \
&removed, &added); \ if (did_add) { \
if (did_add) {\ /*std::cerr << "Adding mergeable update on " << \
/*std::cerr << "Adding mergeable update on " << current_def->def.short_name << " (" << current_def->def.usr << ") for field " << #index_name << std::endl;*/ \ * current_def->def.short_name << " (" << current_def->def.usr << ") for \
query_name.push_back(MergeableUpdate<type_id, type>(current_id_map.ToQuery(current_def->id), added, removed)); \ * field " << #index_name << std::endl;*/ \
} \ query_name.push_back(MergeableUpdate<type_id, type>( \
current_id_map.ToQuery(current_def->id), added, removed)); \
} \
} }
// File // File
files_def_update.push_back(BuildFileDef(current_id_map, current_file)); files_def_update.push_back(BuildFileDef(current_id_map, current_file));
@ -478,115 +446,171 @@ IndexUpdate::IndexUpdate(const IdMap& previous_id_map, const IdMap& current_id_m
// away we don't want to remove the type/func/var usage. // away we don't want to remove the type/func/var usage.
// Types // Types
CompareGroups<IndexType>(previous_file.types, current_file.types, CompareGroups<IndexType>(
/*onRemoved:*/[this, &previous_id_map](IndexType* type) { previous_file.types, current_file.types,
if (type->def.definition_spelling) /*onRemoved:*/
types_removed.push_back(type->def.usr); [this, &previous_id_map](IndexType* type) {
else { if (type->def.definition_spelling)
if (!type->derived.empty()) types_removed.push_back(type->def.usr);
types_derived.push_back(QueryType::DerivedUpdate(previous_id_map.ToQuery(type->id), {}, previous_id_map.ToQuery(type->derived))); else {
if (!type->instances.empty()) if (!type->derived.empty())
types_instances.push_back(QueryType::InstancesUpdate(previous_id_map.ToQuery(type->id), {}, previous_id_map.ToQuery(type->instances))); types_derived.push_back(QueryType::DerivedUpdate(
if (!type->uses.empty()) previous_id_map.ToQuery(type->id), {},
types_uses.push_back(QueryType::UsesUpdate(previous_id_map.ToQuery(type->id), {}, previous_id_map.ToQuery(type->uses))); previous_id_map.ToQuery(type->derived)));
} if (!type->instances.empty())
}, types_instances.push_back(QueryType::InstancesUpdate(
/*onAdded:*/[this, &current_id_map](IndexType* type) { previous_id_map.ToQuery(type->id), {},
optional<QueryType::DefUpdate> def_update = ToQuery(current_id_map, type->def); previous_id_map.ToQuery(type->instances)));
if (def_update) if (!type->uses.empty())
types_def_update.push_back(*def_update); types_uses.push_back(
if (!type->derived.empty()) QueryType::UsesUpdate(previous_id_map.ToQuery(type->id), {},
types_derived.push_back(QueryType::DerivedUpdate(current_id_map.ToQuery(type->id), current_id_map.ToQuery(type->derived))); previous_id_map.ToQuery(type->uses)));
if (!type->instances.empty()) }
types_instances.push_back(QueryType::InstancesUpdate(current_id_map.ToQuery(type->id), current_id_map.ToQuery(type->instances))); },
if (!type->uses.empty()) /*onAdded:*/
types_uses.push_back(QueryType::UsesUpdate(current_id_map.ToQuery(type->id), current_id_map.ToQuery(type->uses))); [this, &current_id_map](IndexType* type) {
}, optional<QueryType::DefUpdate> def_update =
/*onFound:*/[this, &previous_id_map, &current_id_map](IndexType* previous_def, IndexType* current_def) { ToQuery(current_id_map, type->def);
optional<QueryType::DefUpdate> previous_remapped_def = ToQuery(previous_id_map, previous_def->def); if (def_update)
optional<QueryType::DefUpdate> current_remapped_def = ToQuery(current_id_map, current_def->def); types_def_update.push_back(*def_update);
if (current_remapped_def && previous_remapped_def != current_remapped_def && !current_remapped_def->detailed_name.empty()) if (!type->derived.empty())
types_def_update.push_back(*current_remapped_def); types_derived.push_back(
QueryType::DerivedUpdate(current_id_map.ToQuery(type->id),
current_id_map.ToQuery(type->derived)));
if (!type->instances.empty())
types_instances.push_back(QueryType::InstancesUpdate(
current_id_map.ToQuery(type->id),
current_id_map.ToQuery(type->instances)));
if (!type->uses.empty())
types_uses.push_back(
QueryType::UsesUpdate(current_id_map.ToQuery(type->id),
current_id_map.ToQuery(type->uses)));
},
/*onFound:*/
[this, &previous_id_map, &current_id_map](IndexType* previous_def,
IndexType* current_def) {
optional<QueryType::DefUpdate> previous_remapped_def =
ToQuery(previous_id_map, previous_def->def);
optional<QueryType::DefUpdate> current_remapped_def =
ToQuery(current_id_map, current_def->def);
if (current_remapped_def &&
previous_remapped_def != current_remapped_def &&
!current_remapped_def->detailed_name.empty())
types_def_update.push_back(*current_remapped_def);
PROCESS_UPDATE_DIFF(QueryTypeId, types_derived, derived, QueryTypeId); PROCESS_UPDATE_DIFF(QueryTypeId, types_derived, derived, QueryTypeId);
PROCESS_UPDATE_DIFF(QueryTypeId, types_instances, instances, QueryVarId); PROCESS_UPDATE_DIFF(QueryTypeId, types_instances, instances,
PROCESS_UPDATE_DIFF(QueryTypeId, types_uses, uses, QueryLocation); QueryVarId);
}); PROCESS_UPDATE_DIFF(QueryTypeId, types_uses, uses, QueryLocation);
});
// Functions // Functions
CompareGroups<IndexFunc>(previous_file.funcs, current_file.funcs, CompareGroups<IndexFunc>(
/*onRemoved:*/[this, &previous_id_map](IndexFunc* func) { previous_file.funcs, current_file.funcs,
if (func->def.definition_spelling) { /*onRemoved:*/
funcs_removed.push_back(func->def.usr); [this, &previous_id_map](IndexFunc* func) {
} if (func->def.definition_spelling) {
else { funcs_removed.push_back(func->def.usr);
if (!func->declarations.empty()) } else {
funcs_declarations.push_back(QueryFunc::DeclarationsUpdate(previous_id_map.ToQuery(func->id), {}, previous_id_map.ToQuery(func->declarations))); if (!func->declarations.empty())
if (!func->derived.empty()) funcs_declarations.push_back(QueryFunc::DeclarationsUpdate(
funcs_derived.push_back(QueryFunc::DerivedUpdate(previous_id_map.ToQuery(func->id), {}, previous_id_map.ToQuery(func->derived))); previous_id_map.ToQuery(func->id), {},
if (!func->callers.empty()) previous_id_map.ToQuery(func->declarations)));
funcs_callers.push_back(QueryFunc::CallersUpdate(previous_id_map.ToQuery(func->id), {}, previous_id_map.ToQuery(func->callers))); if (!func->derived.empty())
} funcs_derived.push_back(QueryFunc::DerivedUpdate(
}, previous_id_map.ToQuery(func->id), {},
/*onAdded:*/[this, &current_id_map](IndexFunc* func) { previous_id_map.ToQuery(func->derived)));
optional<QueryFunc::DefUpdate> def_update = ToQuery(current_id_map, func->def); if (!func->callers.empty())
if (def_update) funcs_callers.push_back(QueryFunc::CallersUpdate(
funcs_def_update.push_back(*def_update); previous_id_map.ToQuery(func->id), {},
if (!func->declarations.empty()) previous_id_map.ToQuery(func->callers)));
funcs_declarations.push_back(QueryFunc::DeclarationsUpdate(current_id_map.ToQuery(func->id), current_id_map.ToQuery(func->declarations))); }
if (!func->derived.empty()) },
funcs_derived.push_back(QueryFunc::DerivedUpdate(current_id_map.ToQuery(func->id), current_id_map.ToQuery(func->derived))); /*onAdded:*/
if (!func->callers.empty()) [this, &current_id_map](IndexFunc* func) {
funcs_callers.push_back(QueryFunc::CallersUpdate(current_id_map.ToQuery(func->id), current_id_map.ToQuery(func->callers))); optional<QueryFunc::DefUpdate> def_update =
}, ToQuery(current_id_map, func->def);
/*onFound:*/[this, &previous_id_map, &current_id_map](IndexFunc* previous_def, IndexFunc* current_def) { if (def_update)
optional<QueryFunc::DefUpdate> previous_remapped_def = ToQuery(previous_id_map, previous_def->def); funcs_def_update.push_back(*def_update);
optional<QueryFunc::DefUpdate> current_remapped_def = ToQuery(current_id_map, current_def->def); if (!func->declarations.empty())
if (current_remapped_def && previous_remapped_def != current_remapped_def && !current_remapped_def->detailed_name.empty()) funcs_declarations.push_back(QueryFunc::DeclarationsUpdate(
funcs_def_update.push_back(*current_remapped_def); current_id_map.ToQuery(func->id),
current_id_map.ToQuery(func->declarations)));
if (!func->derived.empty())
funcs_derived.push_back(
QueryFunc::DerivedUpdate(current_id_map.ToQuery(func->id),
current_id_map.ToQuery(func->derived)));
if (!func->callers.empty())
funcs_callers.push_back(
QueryFunc::CallersUpdate(current_id_map.ToQuery(func->id),
current_id_map.ToQuery(func->callers)));
},
/*onFound:*/
[this, &previous_id_map, &current_id_map](IndexFunc* previous_def,
IndexFunc* current_def) {
optional<QueryFunc::DefUpdate> previous_remapped_def =
ToQuery(previous_id_map, previous_def->def);
optional<QueryFunc::DefUpdate> current_remapped_def =
ToQuery(current_id_map, current_def->def);
if (current_remapped_def &&
previous_remapped_def != current_remapped_def &&
!current_remapped_def->detailed_name.empty())
funcs_def_update.push_back(*current_remapped_def);
PROCESS_UPDATE_DIFF(QueryFuncId, funcs_declarations, declarations, QueryLocation); PROCESS_UPDATE_DIFF(QueryFuncId, funcs_declarations, declarations,
PROCESS_UPDATE_DIFF(QueryFuncId, funcs_derived, derived, QueryFuncId); QueryLocation);
PROCESS_UPDATE_DIFF(QueryFuncId, funcs_callers, callers, QueryFuncRef); PROCESS_UPDATE_DIFF(QueryFuncId, funcs_derived, derived, QueryFuncId);
}); PROCESS_UPDATE_DIFF(QueryFuncId, funcs_callers, callers, QueryFuncRef);
});
// Variables // Variables
CompareGroups<IndexVar>(previous_file.vars, current_file.vars, CompareGroups<IndexVar>(
/*onRemoved:*/[this, &previous_id_map](IndexVar* var) { previous_file.vars, current_file.vars,
if (var->def.definition_spelling) { /*onRemoved:*/
vars_removed.push_back(var->def.usr); [this, &previous_id_map](IndexVar* var) {
} if (var->def.definition_spelling) {
else { vars_removed.push_back(var->def.usr);
if (!var->uses.empty()) } else {
vars_uses.push_back(QueryVar::UsesUpdate(previous_id_map.ToQuery(var->id), {}, previous_id_map.ToQuery(var->uses))); if (!var->uses.empty())
} vars_uses.push_back(
}, QueryVar::UsesUpdate(previous_id_map.ToQuery(var->id), {},
/*onAdded:*/[this, &current_id_map](IndexVar* var) { previous_id_map.ToQuery(var->uses)));
optional<QueryVar::DefUpdate> def_update = ToQuery(current_id_map, var->def); }
if (def_update) },
vars_def_update.push_back(*def_update); /*onAdded:*/
if (!var->uses.empty()) [this, &current_id_map](IndexVar* var) {
vars_uses.push_back(QueryVar::UsesUpdate(current_id_map.ToQuery(var->id), current_id_map.ToQuery(var->uses))); optional<QueryVar::DefUpdate> def_update =
}, ToQuery(current_id_map, var->def);
/*onFound:*/[this, &previous_id_map, &current_id_map](IndexVar* previous_def, IndexVar* current_def) { if (def_update)
optional<QueryVar::DefUpdate> previous_remapped_def = ToQuery(previous_id_map, previous_def->def); vars_def_update.push_back(*def_update);
optional<QueryVar::DefUpdate> current_remapped_def = ToQuery(current_id_map, current_def->def); if (!var->uses.empty())
if (current_remapped_def && previous_remapped_def != current_remapped_def && !current_remapped_def->detailed_name.empty()) vars_uses.push_back(
vars_def_update.push_back(*current_remapped_def); QueryVar::UsesUpdate(current_id_map.ToQuery(var->id),
current_id_map.ToQuery(var->uses)));
},
/*onFound:*/
[this, &previous_id_map, &current_id_map](IndexVar* previous_def,
IndexVar* current_def) {
optional<QueryVar::DefUpdate> previous_remapped_def =
ToQuery(previous_id_map, previous_def->def);
optional<QueryVar::DefUpdate> current_remapped_def =
ToQuery(current_id_map, current_def->def);
if (current_remapped_def &&
previous_remapped_def != current_remapped_def &&
!current_remapped_def->detailed_name.empty())
vars_def_update.push_back(*current_remapped_def);
PROCESS_UPDATE_DIFF(QueryVarId, vars_uses, uses, QueryLocation); PROCESS_UPDATE_DIFF(QueryVarId, vars_uses, uses, QueryLocation);
}); });
#undef PROCESS_UPDATE_DIFF #undef PROCESS_UPDATE_DIFF
} }
void IndexUpdate::Merge(const IndexUpdate& update) { void IndexUpdate::Merge(const IndexUpdate& update) {
// This function runs on an indexer thread. // This function runs on an indexer thread.
#define INDEX_UPDATE_APPEND(name) \ #define INDEX_UPDATE_APPEND(name) AddRange(&name, update.name);
AddRange(&name, update.name); #define INDEX_UPDATE_MERGE(name) AddMergeableRange(&name, update.name);
#define INDEX_UPDATE_MERGE(name) \
AddMergeableRange(&name, update.name);
INDEX_UPDATE_APPEND(files_removed); INDEX_UPDATE_APPEND(files_removed);
INDEX_UPDATE_APPEND(files_def_update); INDEX_UPDATE_APPEND(files_def_update);
@ -619,37 +643,12 @@ std::string IndexUpdate::ToString() {
return output.GetString(); return output.GetString();
} }
// ------------------------ // ------------------------
// QUERYDB THREAD FUNCTIONS // QUERYDB THREAD FUNCTIONS
// ------------------------ // ------------------------
void QueryDatabase::RemoveUsrs(SymbolKind usr_kind, const std::vector<Usr>& to_remove) { void QueryDatabase::RemoveUsrs(SymbolKind usr_kind,
const std::vector<Usr>& to_remove) {
// This function runs on the querydb thread. // This function runs on the querydb thread.
// When we remove an element, we just erase the state from the storage. We do // When we remove an element, we just erase the state from the storage. We do
@ -690,20 +689,20 @@ void QueryDatabase::RemoveUsrs(SymbolKind usr_kind, const std::vector<Usr>& to_r
} }
void QueryDatabase::ApplyIndexUpdate(IndexUpdate* update) { void QueryDatabase::ApplyIndexUpdate(IndexUpdate* update) {
// This function runs on the querydb thread. // This function runs on the querydb thread.
// Example types: // Example types:
// storage_name => std::vector<optional<QueryType>> // storage_name => std::vector<optional<QueryType>>
// merge_update => QueryType::DerivedUpdate => MergeableUpdate<QueryTypeId, QueryTypeId> // merge_update => QueryType::DerivedUpdate =>
// def => QueryType // MergeableUpdate<QueryTypeId, QueryTypeId> def => QueryType
// def->def_var_name => std::vector<QueryTypeId> // def->def_var_name => std::vector<QueryTypeId>
#define HANDLE_MERGEABLE(update_var_name, def_var_name, storage_name) \ #define HANDLE_MERGEABLE(update_var_name, def_var_name, storage_name) \
for (auto merge_update : update->update_var_name) { \ for (auto merge_update : update->update_var_name) { \
auto& def = storage_name[merge_update.id.id]; \ auto& def = storage_name[merge_update.id.id]; \
if (!def) \ if (!def) \
continue; /* TODO: Should we continue or create an empty def? */ \ continue; /* TODO: Should we continue or create an empty def? */ \
AddRange(&def->def_var_name, merge_update.to_add); \ AddRange(&def->def_var_name, merge_update.to_add); \
RemoveRange(&def->def_var_name, merge_update.to_remove); \ RemoveRange(&def->def_var_name, merge_update.to_remove); \
} }
RemoveUsrs(SymbolKind::File, update->files_removed); RemoveUsrs(SymbolKind::File, update->files_removed);
@ -728,7 +727,8 @@ void QueryDatabase::ApplyIndexUpdate(IndexUpdate* update) {
#undef HANDLE_MERGEABLE #undef HANDLE_MERGEABLE
} }
void QueryDatabase::ImportOrUpdate(const std::vector<QueryFile::DefUpdate>& updates) { void QueryDatabase::ImportOrUpdate(
const std::vector<QueryFile::DefUpdate>& updates) {
// This function runs on the querydb thread. // This function runs on the querydb thread.
for (auto& def : updates) { for (auto& def : updates) {
@ -740,11 +740,13 @@ void QueryDatabase::ImportOrUpdate(const std::vector<QueryFile::DefUpdate>& upda
existing = QueryFile(def.path); existing = QueryFile(def.path);
existing->def = def; existing->def = def;
UpdateDetailedNames(&existing->detailed_name_idx, SymbolKind::File, it->second.id, def.path); UpdateDetailedNames(&existing->detailed_name_idx, SymbolKind::File,
it->second.id, def.path);
} }
} }
void QueryDatabase::ImportOrUpdate(const std::vector<QueryType::DefUpdate>& updates) { void QueryDatabase::ImportOrUpdate(
const std::vector<QueryType::DefUpdate>& updates) {
// This function runs on the querydb thread. // This function runs on the querydb thread.
for (auto& def : updates) { for (auto& def : updates) {
@ -763,11 +765,13 @@ void QueryDatabase::ImportOrUpdate(const std::vector<QueryType::DefUpdate>& upda
continue; continue;
existing->def = def; existing->def = def;
UpdateDetailedNames(&existing->detailed_name_idx, SymbolKind::Type, it->second.id, def.detailed_name); UpdateDetailedNames(&existing->detailed_name_idx, SymbolKind::Type,
it->second.id, def.detailed_name);
} }
} }
void QueryDatabase::ImportOrUpdate(const std::vector<QueryFunc::DefUpdate>& updates) { void QueryDatabase::ImportOrUpdate(
const std::vector<QueryFunc::DefUpdate>& updates) {
// This function runs on the querydb thread. // This function runs on the querydb thread.
for (auto& def : updates) { for (auto& def : updates) {
@ -786,11 +790,13 @@ void QueryDatabase::ImportOrUpdate(const std::vector<QueryFunc::DefUpdate>& upda
continue; continue;
existing->def = def; existing->def = def;
UpdateDetailedNames(&existing->detailed_name_idx, SymbolKind::Func, it->second.id, def.detailed_name); UpdateDetailedNames(&existing->detailed_name_idx, SymbolKind::Func,
it->second.id, def.detailed_name);
} }
} }
void QueryDatabase::ImportOrUpdate(const std::vector<QueryVar::DefUpdate>& updates) { void QueryDatabase::ImportOrUpdate(
const std::vector<QueryVar::DefUpdate>& updates) {
// This function runs on the querydb thread. // This function runs on the querydb thread.
for (auto& def : updates) { for (auto& def : updates) {
@ -810,17 +816,20 @@ void QueryDatabase::ImportOrUpdate(const std::vector<QueryVar::DefUpdate>& updat
existing->def = def; existing->def = def;
if (!def.is_local) if (!def.is_local)
UpdateDetailedNames(&existing->detailed_name_idx, SymbolKind::Var, it->second.id, def.detailed_name); UpdateDetailedNames(&existing->detailed_name_idx, SymbolKind::Var,
it->second.id, def.detailed_name);
} }
} }
void QueryDatabase::UpdateDetailedNames(size_t* qualified_name_index, SymbolKind kind, size_t symbol_index, const std::string& name) { void QueryDatabase::UpdateDetailedNames(size_t* qualified_name_index,
SymbolKind kind,
size_t symbol_index,
const std::string& name) {
if (*qualified_name_index == -1) { if (*qualified_name_index == -1) {
detailed_names.push_back(name); detailed_names.push_back(name);
symbols.push_back(SymbolIdx(kind, symbol_index)); symbols.push_back(SymbolIdx(kind, symbol_index));
*qualified_name_index = detailed_names.size() - 1; *qualified_name_index = detailed_names.size() - 1;
} } else {
else {
detailed_names[*qualified_name_index] = name; detailed_names[*qualified_name_index] = name;
} }
} }
@ -831,31 +840,39 @@ IndexUpdate GetDelta(IndexFile previous, IndexFile current) {
QueryDatabase db; QueryDatabase db;
IdMap previous_map(&db, previous.id_cache); IdMap previous_map(&db, previous.id_cache);
IdMap current_map(&db, current.id_cache); IdMap current_map(&db, current.id_cache);
return IndexUpdate::CreateDelta(&previous_map, &current_map, &previous, &current); return IndexUpdate::CreateDelta(&previous_map, &current_map, &previous,
&current);
} }
TEST_CASE("remove defs") { TEST_CASE("remove defs") {
IndexFile previous("foo.cc"); IndexFile previous("foo.cc");
IndexFile current("foo.cc"); IndexFile current("foo.cc");
previous.Resolve(previous.ToTypeId("usr1"))->def.definition_spelling = Range(Position(1, 0)); previous.Resolve(previous.ToTypeId("usr1"))->def.definition_spelling =
previous.Resolve(previous.ToFuncId("usr2"))->def.definition_spelling = Range(Position(2, 0)); Range(Position(1, 0));
previous.Resolve(previous.ToVarId("usr3"))->def.definition_spelling = Range(Position(3, 0)); previous.Resolve(previous.ToFuncId("usr2"))->def.definition_spelling =
Range(Position(2, 0));
previous.Resolve(previous.ToVarId("usr3"))->def.definition_spelling =
Range(Position(3, 0));
IndexUpdate update = GetDelta(previous, current); IndexUpdate update = GetDelta(previous, current);
REQUIRE(update.types_removed == std::vector<Usr>{ "usr1" }); REQUIRE(update.types_removed == std::vector<Usr>{"usr1"});
REQUIRE(update.funcs_removed == std::vector<Usr>{ "usr2" }); REQUIRE(update.funcs_removed == std::vector<Usr>{"usr2"});
REQUIRE(update.vars_removed == std::vector<Usr>{ "usr3" }); REQUIRE(update.vars_removed == std::vector<Usr>{"usr3"});
} }
TEST_CASE("do not remove ref-only defs") { TEST_CASE("do not remove ref-only defs") {
IndexFile previous("foo.cc"); IndexFile previous("foo.cc");
IndexFile current("foo.cc"); IndexFile current("foo.cc");
previous.Resolve(previous.ToTypeId("usr1"))->uses.push_back(Range(Position(1, 0))); previous.Resolve(previous.ToTypeId("usr1"))
previous.Resolve(previous.ToFuncId("usr2"))->callers.push_back(IndexFuncRef(IndexFuncId(0), Range(Position(2, 0)), false /*is_implicit*/)); ->uses.push_back(Range(Position(1, 0)));
previous.Resolve(previous.ToVarId("usr3"))->uses.push_back(Range(Position(3, 0))); previous.Resolve(previous.ToFuncId("usr2"))
->callers.push_back(IndexFuncRef(IndexFuncId(0), Range(Position(2, 0)),
false /*is_implicit*/));
previous.Resolve(previous.ToVarId("usr3"))
->uses.push_back(Range(Position(3, 0)));
IndexUpdate update = GetDelta(previous, current); IndexUpdate update = GetDelta(previous, current);
@ -871,8 +888,10 @@ TEST_CASE("func callers") {
IndexFunc* pf = previous.Resolve(previous.ToFuncId("usr")); IndexFunc* pf = previous.Resolve(previous.ToFuncId("usr"));
IndexFunc* cf = current.Resolve(current.ToFuncId("usr")); IndexFunc* cf = current.Resolve(current.ToFuncId("usr"));
pf->callers.push_back(IndexFuncRef(IndexFuncId(0), Range(Position(1, 0)), false /*is_implicit*/)); pf->callers.push_back(IndexFuncRef(IndexFuncId(0), Range(Position(1, 0)),
cf->callers.push_back(IndexFuncRef(IndexFuncId(0), Range(Position(2, 0)), false /*is_implicit*/)); false /*is_implicit*/));
cf->callers.push_back(IndexFuncRef(IndexFuncId(0), Range(Position(2, 0)),
false /*is_implicit*/));
IndexUpdate update = GetDelta(previous, current); IndexUpdate update = GetDelta(previous, current);
@ -880,7 +899,8 @@ TEST_CASE("func callers") {
REQUIRE(update.funcs_callers.size() == 1); REQUIRE(update.funcs_callers.size() == 1);
REQUIRE(update.funcs_callers[0].id == QueryFuncId(0)); REQUIRE(update.funcs_callers[0].id == QueryFuncId(0));
REQUIRE(update.funcs_callers[0].to_remove.size() == 1); REQUIRE(update.funcs_callers[0].to_remove.size() == 1);
REQUIRE(update.funcs_callers[0].to_remove[0].loc.range == Range(Position(1, 0))); REQUIRE(update.funcs_callers[0].to_remove[0].loc.range ==
Range(Position(1, 0)));
REQUIRE(update.funcs_callers[0].to_add.size() == 1); REQUIRE(update.funcs_callers[0].to_add.size() == 1);
REQUIRE(update.funcs_callers[0].to_add[0].loc.range == Range(Position(2, 0))); REQUIRE(update.funcs_callers[0].to_add[0].loc.range == Range(Position(2, 0)));
} }
@ -912,18 +932,24 @@ TEST_CASE("apply delta") {
IndexFunc* pf = previous.Resolve(previous.ToFuncId("usr")); IndexFunc* pf = previous.Resolve(previous.ToFuncId("usr"));
IndexFunc* cf = current.Resolve(current.ToFuncId("usr")); IndexFunc* cf = current.Resolve(current.ToFuncId("usr"));
pf->callers.push_back(IndexFuncRef(IndexFuncId(0), Range(Position(1, 0)), false /*is_implicit*/)); pf->callers.push_back(IndexFuncRef(IndexFuncId(0), Range(Position(1, 0)),
pf->callers.push_back(IndexFuncRef(IndexFuncId(0), Range(Position(2, 0)), false /*is_implicit*/)); false /*is_implicit*/));
cf->callers.push_back(IndexFuncRef(IndexFuncId(0), Range(Position(4, 0)), false /*is_implicit*/)); pf->callers.push_back(IndexFuncRef(IndexFuncId(0), Range(Position(2, 0)),
cf->callers.push_back(IndexFuncRef(IndexFuncId(0), Range(Position(5, 0)), false /*is_implicit*/)); false /*is_implicit*/));
cf->callers.push_back(IndexFuncRef(IndexFuncId(0), Range(Position(4, 0)),
false /*is_implicit*/));
cf->callers.push_back(IndexFuncRef(IndexFuncId(0), Range(Position(5, 0)),
false /*is_implicit*/));
QueryDatabase db; QueryDatabase db;
IdMap previous_map(&db, previous.id_cache); IdMap previous_map(&db, previous.id_cache);
IdMap current_map(&db, current.id_cache); IdMap current_map(&db, current.id_cache);
REQUIRE(db.funcs.size() == 1); REQUIRE(db.funcs.size() == 1);
IndexUpdate import_update = IndexUpdate::CreateDelta(nullptr, &previous_map, nullptr, &previous); IndexUpdate import_update =
IndexUpdate delta_update = IndexUpdate::CreateDelta(&previous_map, &current_map, &previous, &current); IndexUpdate::CreateDelta(nullptr, &previous_map, nullptr, &previous);
IndexUpdate delta_update = IndexUpdate::CreateDelta(
&previous_map, &current_map, &previous, &current);
db.ApplyIndexUpdate(&import_update); db.ApplyIndexUpdate(&import_update);
REQUIRE(db.funcs[0]->callers.size() == 2); REQUIRE(db.funcs[0]->callers.size() == 2);

View File

@ -20,22 +20,14 @@ using QueryTypeId = Id<QueryType>;
using QueryFuncId = Id<QueryFunc>; using QueryFuncId = Id<QueryFunc>;
using QueryVarId = Id<QueryVar>; using QueryVarId = Id<QueryVar>;
struct IdMap; struct IdMap;
struct QueryLocation { struct QueryLocation {
QueryFileId path; QueryFileId path;
Range range; Range range;
QueryLocation() {} // Do not use, needed for reflect. QueryLocation() {} // Do not use, needed for reflect.
QueryLocation(QueryFileId path, Range range) QueryLocation(QueryFileId path, Range range) : path(path), range(range) {}
: path(path), range(range) {}
QueryLocation OffsetStartColumn(int16_t offset) const { QueryLocation OffsetStartColumn(int16_t offset) const {
QueryLocation result = *this; QueryLocation result = *this;
@ -45,11 +37,11 @@ struct QueryLocation {
bool operator==(const QueryLocation& other) const { bool operator==(const QueryLocation& other) const {
// Note: We ignore |is_interesting|. // Note: We ignore |is_interesting|.
return return path == other.path && range == other.range;
path == other.path && }
range == other.range; bool operator!=(const QueryLocation& other) const {
return !(*this == other);
} }
bool operator!=(const QueryLocation& other) const { return !(*this == other); }
bool operator<(const QueryLocation& o) const { bool operator<(const QueryLocation& o) const {
if (path < o.path) if (path < o.path)
return true; return true;
@ -66,7 +58,9 @@ struct SymbolIdx {
SymbolKind kind; SymbolKind kind;
size_t idx; size_t idx;
SymbolIdx() : kind(SymbolKind::Invalid), idx((size_t)-1) {} // Default ctor needed by stdlib. Do not use. SymbolIdx()
: kind(SymbolKind::Invalid),
idx((size_t)-1) {} // Default ctor needed by stdlib. Do not use.
SymbolIdx(SymbolKind kind, uint64_t idx) : kind(kind), idx(idx) {} SymbolIdx(SymbolKind kind, uint64_t idx) : kind(kind), idx(idx) {}
bool operator==(const SymbolIdx& that) const { bool operator==(const SymbolIdx& that) const {
@ -85,7 +79,7 @@ struct SymbolRef {
SymbolIdx idx; SymbolIdx idx;
QueryLocation loc; QueryLocation loc;
SymbolRef() {} // Do not use, needed for reflect. SymbolRef() {} // Do not use, needed for reflect.
SymbolRef(SymbolIdx idx, QueryLocation loc) : idx(idx), loc(loc) {} SymbolRef(SymbolIdx idx, QueryLocation loc) : idx(idx), loc(loc) {}
bool operator==(const SymbolRef& that) const { bool operator==(const SymbolRef& that) const {
@ -108,11 +102,13 @@ struct QueryFuncRef {
bool has_id() const { return id_.id != -1; } bool has_id() const { return id_.id != -1; }
QueryFuncRef() {} // Do not use, needed for reflect. QueryFuncRef() {} // Do not use, needed for reflect.
QueryFuncRef(QueryFuncId id, QueryLocation loc, bool is_implicit) : id_(id), loc(loc), is_implicit(is_implicit) {} QueryFuncRef(QueryFuncId id, QueryLocation loc, bool is_implicit)
: id_(id), loc(loc), is_implicit(is_implicit) {}
bool operator==(const QueryFuncRef& that) const { bool operator==(const QueryFuncRef& that) const {
return id_ == that.id_ && loc == that.loc && is_implicit == that.is_implicit; return id_ == that.id_ && loc == that.loc &&
is_implicit == that.is_implicit;
} }
bool operator!=(const QueryFuncRef& that) const { return !(*this == that); } bool operator!=(const QueryFuncRef& that) const { return !(*this == that); }
bool operator<(const QueryFuncRef& that) const { bool operator<(const QueryFuncRef& that) const {
@ -120,7 +116,8 @@ struct QueryFuncRef {
return true; return true;
if (id_ == that.id_ && loc.range.start < that.loc.range.start) if (id_ == that.id_ && loc.range.start < that.loc.range.start)
return true; return true;
return id_ == that.id_ && loc.range.start == that.loc.range.start && is_implicit < that.is_implicit; return id_ == that.id_ && loc.range.start == that.loc.range.start &&
is_implicit < that.is_implicit;
} }
}; };
MAKE_REFLECT_STRUCT(QueryFuncRef, id_, loc, is_implicit); MAKE_REFLECT_STRUCT(QueryFuncRef, id_, loc, is_implicit);
@ -135,7 +132,7 @@ MAKE_REFLECT_STRUCT(QueryFuncRef, id_, loc, is_implicit);
// that it can be merged with other updates before actually being applied to // that it can be merged with other updates before actually being applied to
// the main database. See |MergeableUpdate|. // the main database. See |MergeableUpdate|.
template<typename TId, typename TValue> template <typename TId, typename TValue>
struct MergeableUpdate { struct MergeableUpdate {
// The type/func/var which is getting new usages. // The type/func/var which is getting new usages.
TId id; TId id;
@ -144,11 +141,13 @@ struct MergeableUpdate {
std::vector<TValue> to_remove; std::vector<TValue> to_remove;
MergeableUpdate(TId id, const std::vector<TValue>& to_add) MergeableUpdate(TId id, const std::vector<TValue>& to_add)
: id(id), to_add(to_add) {} : id(id), to_add(to_add) {}
MergeableUpdate(TId id, const std::vector<TValue>& to_add, const std::vector<TValue>& to_remove) MergeableUpdate(TId id,
: id(id), to_add(to_add), to_remove(to_remove) {} const std::vector<TValue>& to_add,
const std::vector<TValue>& to_remove)
: id(id), to_add(to_add), to_remove(to_remove) {}
}; };
template<typename TVisitor, typename TId, typename TValue> template <typename TVisitor, typename TId, typename TValue>
void Reflect(TVisitor& visitor, MergeableUpdate<TId, TValue>& value) { void Reflect(TVisitor& visitor, MergeableUpdate<TId, TValue>& value) {
REFLECT_MEMBER_START(); REFLECT_MEMBER_START();
REFLECT_MEMBER(id); REFLECT_MEMBER(id);
@ -178,7 +177,10 @@ struct QueryFile {
MAKE_REFLECT_STRUCT(QueryFile::Def, path, outline, all_symbols); MAKE_REFLECT_STRUCT(QueryFile::Def, path, outline, all_symbols);
struct QueryType { struct QueryType {
using DefUpdate = TypeDefDefinitionData<QueryTypeId, QueryFuncId, QueryVarId, QueryLocation>; using DefUpdate = TypeDefDefinitionData<QueryTypeId,
QueryFuncId,
QueryVarId,
QueryLocation>;
using DerivedUpdate = MergeableUpdate<QueryTypeId, QueryTypeId>; using DerivedUpdate = MergeableUpdate<QueryTypeId, QueryTypeId>;
using InstancesUpdate = MergeableUpdate<QueryTypeId, QueryVarId>; using InstancesUpdate = MergeableUpdate<QueryTypeId, QueryVarId>;
using UsesUpdate = MergeableUpdate<QueryTypeId, QueryLocation>; using UsesUpdate = MergeableUpdate<QueryTypeId, QueryLocation>;
@ -193,7 +195,11 @@ struct QueryType {
}; };
struct QueryFunc { struct QueryFunc {
using DefUpdate = FuncDefDefinitionData<QueryTypeId, QueryFuncId, QueryVarId, QueryFuncRef, QueryLocation>; using DefUpdate = FuncDefDefinitionData<QueryTypeId,
QueryFuncId,
QueryVarId,
QueryFuncRef,
QueryLocation>;
using DeclarationsUpdate = MergeableUpdate<QueryFuncId, QueryLocation>; using DeclarationsUpdate = MergeableUpdate<QueryFuncId, QueryLocation>;
using DerivedUpdate = MergeableUpdate<QueryFuncId, QueryFuncId>; using DerivedUpdate = MergeableUpdate<QueryFuncId, QueryFuncId>;
using CallersUpdate = MergeableUpdate<QueryFuncId, QueryFuncRef>; using CallersUpdate = MergeableUpdate<QueryFuncId, QueryFuncRef>;
@ -208,7 +214,8 @@ struct QueryFunc {
}; };
struct QueryVar { struct QueryVar {
using DefUpdate = VarDefDefinitionData<QueryTypeId, QueryFuncId, QueryVarId, QueryLocation>; using DefUpdate =
VarDefDefinitionData<QueryTypeId, QueryFuncId, QueryVarId, QueryLocation>;
using UsesUpdate = MergeableUpdate<QueryVarId, QueryLocation>; using UsesUpdate = MergeableUpdate<QueryVarId, QueryLocation>;
DefUpdate def; DefUpdate def;
@ -221,7 +228,10 @@ struct QueryVar {
struct IndexUpdate { struct IndexUpdate {
// Creates a new IndexUpdate based on the delta from previous to current. If // Creates a new IndexUpdate based on the delta from previous to current. If
// no delta computation should be done just pass null for previous. // no delta computation should be done just pass null for previous.
static IndexUpdate CreateDelta(const IdMap* previous_id_map, const IdMap* current_id_map, IndexFile* previous, IndexFile* current); static IndexUpdate CreateDelta(const IdMap* previous_id_map,
const IdMap* current_id_map,
IndexFile* previous,
IndexFile* current);
// Merge |update| into this update; this can reduce overhead / index update // Merge |update| into this update; this can reduce overhead / index update
// work can be parallelized. // work can be parallelized.
@ -257,13 +267,27 @@ struct IndexUpdate {
// Creates an index update assuming that |previous| is already // Creates an index update assuming that |previous| is already
// in the index, so only the delta between |previous| and |current| // in the index, so only the delta between |previous| and |current|
// will be applied. // will be applied.
IndexUpdate(const IdMap& previous_id_map, const IdMap& current_id_map, IndexFile& previous, IndexFile& current); IndexUpdate(const IdMap& previous_id_map,
const IdMap& current_id_map,
IndexFile& previous,
IndexFile& current);
}; };
// NOTICE: We're not reflecting on files_removed or files_def_update, it is too much output when logging // NOTICE: We're not reflecting on files_removed or files_def_update, it is too
// much output when logging
MAKE_REFLECT_STRUCT(IndexUpdate, MAKE_REFLECT_STRUCT(IndexUpdate,
types_removed, types_def_update, types_derived, types_instances, types_uses, types_removed,
funcs_removed, funcs_def_update, funcs_declarations, funcs_derived, funcs_callers, types_def_update,
vars_removed, vars_def_update, vars_uses); types_derived,
types_instances,
types_uses,
funcs_removed,
funcs_def_update,
funcs_declarations,
funcs_derived,
funcs_callers,
vars_removed,
vars_def_update,
vars_uses);
// The query database is heavily optimized for fast queries. It is stored // The query database is heavily optimized for fast queries. It is stored
// in-memory. // in-memory.
@ -295,12 +319,12 @@ struct QueryDatabase {
void ImportOrUpdate(const std::vector<QueryType::DefUpdate>& updates); void ImportOrUpdate(const std::vector<QueryType::DefUpdate>& updates);
void ImportOrUpdate(const std::vector<QueryFunc::DefUpdate>& updates); void ImportOrUpdate(const std::vector<QueryFunc::DefUpdate>& updates);
void ImportOrUpdate(const std::vector<QueryVar::DefUpdate>& updates); void ImportOrUpdate(const std::vector<QueryVar::DefUpdate>& updates);
void UpdateDetailedNames(size_t* qualified_name_index, SymbolKind kind, size_t symbol_index, const std::string& name); void UpdateDetailedNames(size_t* qualified_name_index,
SymbolKind kind,
size_t symbol_index,
const std::string& name);
}; };
struct IdMap { struct IdMap {
const IdCache& local_ids; const IdCache& local_ids;
QueryFileId primary_file; QueryFileId primary_file;
@ -324,12 +348,14 @@ struct IdMap {
std::vector<QueryFuncId> ToQuery(std::vector<IndexFuncId> ids) const; std::vector<QueryFuncId> ToQuery(std::vector<IndexFuncId> ids) const;
std::vector<QueryVarId> ToQuery(std::vector<IndexVarId> ids) const; std::vector<QueryVarId> ToQuery(std::vector<IndexVarId> ids) const;
std::vector<QueryFuncRef> ToQuery(std::vector<IndexFuncRef> refs) const; std::vector<QueryFuncRef> ToQuery(std::vector<IndexFuncRef> refs) const;
std::vector<QueryLocation> ToQuery(std::vector<IndexFunc::Declaration> decls) const; std::vector<QueryLocation> ToQuery(
std::vector<IndexFunc::Declaration> decls) const;
SymbolIdx ToSymbol(IndexTypeId id) const; SymbolIdx ToSymbol(IndexTypeId id) const;
SymbolIdx ToSymbol(IndexFuncId id) const; SymbolIdx ToSymbol(IndexFuncId id) const;
SymbolIdx ToSymbol(IndexVarId id) const; SymbolIdx ToSymbol(IndexVarId id) const;
private:
private:
spp::sparse_hash_map<IndexTypeId, QueryTypeId> cached_type_ids_; spp::sparse_hash_map<IndexTypeId, QueryTypeId> cached_type_ids_;
spp::sparse_hash_map<IndexFuncId, QueryFuncId> cached_func_ids_; spp::sparse_hash_map<IndexFuncId, QueryFuncId> cached_func_ids_;
spp::sparse_hash_map<IndexVarId, QueryVarId> cached_var_ids_; spp::sparse_hash_map<IndexVarId, QueryVarId> cached_var_ids_;

View File

@ -13,159 +13,170 @@ int ComputeRangeSize(const Range& range) {
} // namespace } // namespace
optional<QueryLocation> GetDefinitionSpellingOfSymbol(QueryDatabase* db, const QueryTypeId& id) { optional<QueryLocation> GetDefinitionSpellingOfSymbol(QueryDatabase* db,
const QueryTypeId& id) {
optional<QueryType>& type = db->types[id.id]; optional<QueryType>& type = db->types[id.id];
if (type) if (type)
return type->def.definition_spelling; return type->def.definition_spelling;
return nullopt; return nullopt;
} }
optional<QueryLocation> GetDefinitionSpellingOfSymbol(QueryDatabase* db, const QueryFuncId& id) { optional<QueryLocation> GetDefinitionSpellingOfSymbol(QueryDatabase* db,
const QueryFuncId& id) {
optional<QueryFunc>& func = db->funcs[id.id]; optional<QueryFunc>& func = db->funcs[id.id];
if (func) if (func)
return func->def.definition_spelling; return func->def.definition_spelling;
return nullopt; return nullopt;
} }
optional<QueryLocation> GetDefinitionSpellingOfSymbol(QueryDatabase* db, const QueryVarId& id) { optional<QueryLocation> GetDefinitionSpellingOfSymbol(QueryDatabase* db,
const QueryVarId& id) {
optional<QueryVar>& var = db->vars[id.id]; optional<QueryVar>& var = db->vars[id.id];
if (var) if (var)
return var->def.definition_spelling; return var->def.definition_spelling;
return nullopt; return nullopt;
} }
optional<QueryLocation> GetDefinitionSpellingOfSymbol(QueryDatabase* db, const SymbolIdx& symbol) { optional<QueryLocation> GetDefinitionSpellingOfSymbol(QueryDatabase* db,
const SymbolIdx& symbol) {
switch (symbol.kind) { switch (symbol.kind) {
case SymbolKind::Type: { case SymbolKind::Type: {
optional<QueryType>& type = db->types[symbol.idx]; optional<QueryType>& type = db->types[symbol.idx];
if (type) if (type)
return type->def.definition_spelling; return type->def.definition_spelling;
break; break;
} }
case SymbolKind::Func: { case SymbolKind::Func: {
optional<QueryFunc>& func = db->funcs[symbol.idx]; optional<QueryFunc>& func = db->funcs[symbol.idx];
if (func) if (func)
return func->def.definition_spelling; return func->def.definition_spelling;
break; break;
} }
case SymbolKind::Var: { case SymbolKind::Var: {
optional<QueryVar>& var = db->vars[symbol.idx]; optional<QueryVar>& var = db->vars[symbol.idx];
if (var) if (var)
return var->def.definition_spelling; return var->def.definition_spelling;
break; break;
} }
case SymbolKind::File: case SymbolKind::File:
case SymbolKind::Invalid: { case SymbolKind::Invalid: {
assert(false && "unexpected"); assert(false && "unexpected");
break; break;
} }
} }
return nullopt; return nullopt;
} }
optional<QueryLocation> GetDefinitionExtentOfSymbol(QueryDatabase* db, const SymbolIdx& symbol) { optional<QueryLocation> GetDefinitionExtentOfSymbol(QueryDatabase* db,
const SymbolIdx& symbol) {
switch (symbol.kind) { switch (symbol.kind) {
case SymbolKind::Type: { case SymbolKind::Type: {
optional<QueryType>& type = db->types[symbol.idx]; optional<QueryType>& type = db->types[symbol.idx];
if (type) if (type)
return type->def.definition_extent; return type->def.definition_extent;
break; break;
} }
case SymbolKind::Func: { case SymbolKind::Func: {
optional<QueryFunc>& func = db->funcs[symbol.idx]; optional<QueryFunc>& func = db->funcs[symbol.idx];
if (func) if (func)
return func->def.definition_extent; return func->def.definition_extent;
break; break;
} }
case SymbolKind::Var: { case SymbolKind::Var: {
optional<QueryVar>& var = db->vars[symbol.idx]; optional<QueryVar>& var = db->vars[symbol.idx];
if (var) if (var)
return var->def.definition_extent; return var->def.definition_extent;
break; break;
} }
case SymbolKind::File: { case SymbolKind::File: {
return QueryLocation(QueryFileId(symbol.idx), Range(Position(1, 1), Position(1, 1))); return QueryLocation(QueryFileId(symbol.idx),
} Range(Position(1, 1), Position(1, 1)));
case SymbolKind::Invalid: { }
assert(false && "unexpected"); case SymbolKind::Invalid: {
break; assert(false && "unexpected");
} break;
}
} }
return nullopt; return nullopt;
} }
std::string GetHoverForSymbol(QueryDatabase* db, const SymbolIdx& symbol) { std::string GetHoverForSymbol(QueryDatabase* db, const SymbolIdx& symbol) {
switch (symbol.kind) { switch (symbol.kind) {
case SymbolKind::Type: { case SymbolKind::Type: {
optional<QueryType>& type = db->types[symbol.idx]; optional<QueryType>& type = db->types[symbol.idx];
if (type) if (type)
return type->def.detailed_name; return type->def.detailed_name;
break; break;
} }
case SymbolKind::Func: { case SymbolKind::Func: {
optional<QueryFunc>& func = db->funcs[symbol.idx]; optional<QueryFunc>& func = db->funcs[symbol.idx];
if (func) if (func)
return func->def.detailed_name; return func->def.detailed_name;
break; break;
} }
case SymbolKind::Var: { case SymbolKind::Var: {
optional<QueryVar>& var = db->vars[symbol.idx]; optional<QueryVar>& var = db->vars[symbol.idx];
if (var) if (var)
return var->def.detailed_name; return var->def.detailed_name;
break; break;
} }
case SymbolKind::File: case SymbolKind::File:
case SymbolKind::Invalid: { case SymbolKind::Invalid: {
assert(false && "unexpected"); assert(false && "unexpected");
break; break;
} }
} }
return ""; return "";
} }
optional<QueryFileId> GetDeclarationFileForSymbol(QueryDatabase* db, const SymbolIdx& symbol) { optional<QueryFileId> GetDeclarationFileForSymbol(QueryDatabase* db,
const SymbolIdx& symbol) {
switch (symbol.kind) { switch (symbol.kind) {
case SymbolKind::Type: { case SymbolKind::Type: {
optional<QueryType>& type = db->types[symbol.idx]; optional<QueryType>& type = db->types[symbol.idx];
if (type && type->def.definition_spelling) if (type && type->def.definition_spelling)
return type->def.definition_spelling->path; return type->def.definition_spelling->path;
break; break;
} }
case SymbolKind::Func: { case SymbolKind::Func: {
optional<QueryFunc>& func = db->funcs[symbol.idx]; optional<QueryFunc>& func = db->funcs[symbol.idx];
if (func) { if (func) {
if (!func->declarations.empty()) if (!func->declarations.empty())
return func->declarations[0].path; return func->declarations[0].path;
if (func->def.definition_spelling) if (func->def.definition_spelling)
return func->def.definition_spelling->path; return func->def.definition_spelling->path;
}
break;
}
case SymbolKind::Var: {
optional<QueryVar>& var = db->vars[symbol.idx];
if (var && var->def.definition_spelling)
return var->def.definition_spelling->path;
break;
}
case SymbolKind::File: {
return QueryFileId(symbol.idx);
}
case SymbolKind::Invalid: {
assert(false && "unexpected");
break;
} }
break;
}
case SymbolKind::Var: {
optional<QueryVar>& var = db->vars[symbol.idx];
if (var && var->def.definition_spelling)
return var->def.definition_spelling->path;
break;
}
case SymbolKind::File: {
return QueryFileId(symbol.idx);
}
case SymbolKind::Invalid: {
assert(false && "unexpected");
break;
}
} }
return nullopt; return nullopt;
} }
std::vector<QueryLocation> ToQueryLocation(QueryDatabase* db, const std::vector<QueryFuncRef>& refs) { std::vector<QueryLocation> ToQueryLocation(
QueryDatabase* db,
const std::vector<QueryFuncRef>& refs) {
std::vector<QueryLocation> locs; std::vector<QueryLocation> locs;
locs.reserve(refs.size()); locs.reserve(refs.size());
for (const QueryFuncRef& ref : refs) for (const QueryFuncRef& ref : refs)
locs.push_back(ref.loc); locs.push_back(ref.loc);
return locs; return locs;
} }
std::vector<QueryLocation> ToQueryLocation(QueryDatabase* db, const std::vector<QueryTypeId>& ids) { std::vector<QueryLocation> ToQueryLocation(
QueryDatabase* db,
const std::vector<QueryTypeId>& ids) {
std::vector<QueryLocation> locs; std::vector<QueryLocation> locs;
locs.reserve(ids.size()); locs.reserve(ids.size());
for (const QueryTypeId& id : ids) { for (const QueryTypeId& id : ids) {
@ -175,7 +186,9 @@ std::vector<QueryLocation> ToQueryLocation(QueryDatabase* db, const std::vector<
} }
return locs; return locs;
} }
std::vector<QueryLocation> ToQueryLocation(QueryDatabase* db, const std::vector<QueryFuncId>& ids) { std::vector<QueryLocation> ToQueryLocation(
QueryDatabase* db,
const std::vector<QueryFuncId>& ids) {
std::vector<QueryLocation> locs; std::vector<QueryLocation> locs;
locs.reserve(ids.size()); locs.reserve(ids.size());
for (const QueryFuncId& id : ids) { for (const QueryFuncId& id : ids) {
@ -185,7 +198,8 @@ std::vector<QueryLocation> ToQueryLocation(QueryDatabase* db, const std::vector<
} }
return locs; return locs;
} }
std::vector<QueryLocation> ToQueryLocation(QueryDatabase* db, const std::vector<QueryVarId>& ids) { std::vector<QueryLocation> ToQueryLocation(QueryDatabase* db,
const std::vector<QueryVarId>& ids) {
std::vector<QueryLocation> locs; std::vector<QueryLocation> locs;
locs.reserve(ids.size()); locs.reserve(ids.size());
for (const QueryVarId& id : ids) { for (const QueryVarId& id : ids) {
@ -196,81 +210,84 @@ std::vector<QueryLocation> ToQueryLocation(QueryDatabase* db, const std::vector<
return locs; return locs;
} }
std::vector<QueryLocation> GetUsesOfSymbol(QueryDatabase* db,
const SymbolIdx& symbol) {
std::vector<QueryLocation> GetUsesOfSymbol(QueryDatabase* db, const SymbolIdx& symbol) {
switch (symbol.kind) { switch (symbol.kind) {
case SymbolKind::Type: { case SymbolKind::Type: {
optional<QueryType>& type = db->types[symbol.idx]; optional<QueryType>& type = db->types[symbol.idx];
if (type) if (type)
return type->uses; return type->uses;
break; break;
} }
case SymbolKind::Func: { case SymbolKind::Func: {
// TODO: the vector allocation could be avoided. // TODO: the vector allocation could be avoided.
optional<QueryFunc>& func = db->funcs[symbol.idx]; optional<QueryFunc>& func = db->funcs[symbol.idx];
if (func) { if (func) {
std::vector<QueryLocation> result = ToQueryLocation(db, func->callers); std::vector<QueryLocation> result = ToQueryLocation(db, func->callers);
AddRange(&result, func->declarations); AddRange(&result, func->declarations);
if (func->def.definition_spelling) if (func->def.definition_spelling)
result.push_back(*func->def.definition_spelling); result.push_back(*func->def.definition_spelling);
return result; return result;
}
break;
}
case SymbolKind::Var: {
optional<QueryVar>& var = db->vars[symbol.idx];
if (var)
return var->uses;
break;
}
case SymbolKind::File:
case SymbolKind::Invalid: {
assert(false && "unexpected");
break;
} }
break;
}
case SymbolKind::Var: {
optional<QueryVar>& var = db->vars[symbol.idx];
if (var)
return var->uses;
break;
}
case SymbolKind::File:
case SymbolKind::Invalid: {
assert(false && "unexpected");
break;
}
} }
return {}; return {};
} }
std::vector<QueryLocation> GetDeclarationsOfSymbolForGotoDefinition(QueryDatabase* db, const SymbolIdx& symbol) { std::vector<QueryLocation> GetDeclarationsOfSymbolForGotoDefinition(
QueryDatabase* db,
const SymbolIdx& symbol) {
switch (symbol.kind) { switch (symbol.kind) {
case SymbolKind::Type: { case SymbolKind::Type: {
// Returning the definition spelling of a type is a hack (and is why the // Returning the definition spelling of a type is a hack (and is why the
// function has the postfix `ForGotoDefintion`, but it lets the user // function has the postfix `ForGotoDefintion`, but it lets the user
// jump to the start of a type if clicking goto-definition on the same // jump to the start of a type if clicking goto-definition on the same
// type from within the type definition. // type from within the type definition.
optional<QueryType>& type = db->types[symbol.idx]; optional<QueryType>& type = db->types[symbol.idx];
if (type) { if (type) {
optional<QueryLocation> declaration = type->def.definition_spelling; optional<QueryLocation> declaration = type->def.definition_spelling;
if (declaration) if (declaration)
return { *declaration }; return {*declaration};
}
break;
} }
break; case SymbolKind::Func: {
} optional<QueryFunc>& func = db->funcs[symbol.idx];
case SymbolKind::Func: { if (func)
optional<QueryFunc>& func = db->funcs[symbol.idx]; return func->declarations;
if (func) break;
return func->declarations;
break;
}
case SymbolKind::Var: {
optional<QueryVar>& var = db->vars[symbol.idx];
if (var) {
optional<QueryLocation> declaration = var->def.declaration;
if (declaration)
return { *declaration };
} }
break; case SymbolKind::Var: {
} optional<QueryVar>& var = db->vars[symbol.idx];
default: if (var) {
break; optional<QueryLocation> declaration = var->def.declaration;
if (declaration)
return {*declaration};
}
break;
}
default:
break;
} }
return {}; return {};
} }
optional<QueryLocation> GetBaseDefinitionOrDeclarationSpelling(QueryDatabase* db, QueryFunc& func) { optional<QueryLocation> GetBaseDefinitionOrDeclarationSpelling(
QueryDatabase* db,
QueryFunc& func) {
if (!func.def.base) if (!func.def.base)
return nullopt; return nullopt;
optional<QueryFunc>& base = db->funcs[func.def.base->id]; optional<QueryFunc>& base = db->funcs[func.def.base->id];
@ -316,7 +333,8 @@ bool HasCallersOnSelfOrBaseOrDerived(QueryDatabase* db, QueryFunc& root) {
return false; return false;
} }
std::vector<QueryFuncRef> GetCallersForAllBaseFunctions(QueryDatabase* db, QueryFunc& root) { std::vector<QueryFuncRef> GetCallersForAllBaseFunctions(QueryDatabase* db,
QueryFunc& root) {
std::vector<QueryFuncRef> callers; std::vector<QueryFuncRef> callers;
optional<QueryFuncId> func_id = root.def.base; optional<QueryFuncId> func_id = root.def.base;
@ -332,7 +350,8 @@ std::vector<QueryFuncRef> GetCallersForAllBaseFunctions(QueryDatabase* db, Query
return callers; return callers;
} }
std::vector<QueryFuncRef> GetCallersForAllDerivedFunctions(QueryDatabase* db, QueryFunc& root) { std::vector<QueryFuncRef> GetCallersForAllDerivedFunctions(QueryDatabase* db,
QueryFunc& root) {
std::vector<QueryFuncRef> callers; std::vector<QueryFuncRef> callers;
std::queue<QueryFuncId> queue; std::queue<QueryFuncId> queue;
@ -351,7 +370,8 @@ std::vector<QueryFuncRef> GetCallersForAllDerivedFunctions(QueryDatabase* db, Qu
return callers; return callers;
} }
optional<lsPosition> GetLsPosition(WorkingFile* working_file, const Position& position) { optional<lsPosition> GetLsPosition(WorkingFile* working_file,
const Position& position) {
if (!working_file) if (!working_file)
return lsPosition(position.line - 1, position.column - 1); return lsPosition(position.line - 1, position.column - 1);
@ -365,12 +385,14 @@ optional<lsPosition> GetLsPosition(WorkingFile* working_file, const Position& po
optional<lsRange> GetLsRange(WorkingFile* working_file, const Range& location) { optional<lsRange> GetLsRange(WorkingFile* working_file, const Range& location) {
if (!working_file) { if (!working_file) {
return lsRange( return lsRange(
lsPosition(location.start.line - 1, location.start.column - 1), lsPosition(location.start.line - 1, location.start.column - 1),
lsPosition(location.end.line - 1, location.end.column - 1)); lsPosition(location.end.line - 1, location.end.column - 1));
} }
optional<int> start = working_file->GetBufferLineFromIndexLine(location.start.line); optional<int> start =
optional<int> end = working_file->GetBufferLineFromIndexLine(location.end.line); working_file->GetBufferLineFromIndexLine(location.start.line);
optional<int> end =
working_file->GetBufferLineFromIndexLine(location.end.line);
if (!start || !end) if (!start || !end)
return nullopt; return nullopt;
@ -383,18 +405,18 @@ optional<lsRange> GetLsRange(WorkingFile* working_file, const Range& location) {
if (*end < *start) if (*end < *start)
*end = *start + (location.end.line - location.start.line); *end = *start + (location.end.line - location.start.line);
return lsRange( return lsRange(lsPosition(*start - 1, location.start.column - 1),
lsPosition(*start - 1, location.start.column - 1), lsPosition(*end - 1, location.end.column - 1));
lsPosition(*end - 1, location.end.column - 1));
} }
lsDocumentUri GetLsDocumentUri(QueryDatabase* db, QueryFileId file_id, std::string* path) { lsDocumentUri GetLsDocumentUri(QueryDatabase* db,
QueryFileId file_id,
std::string* path) {
optional<QueryFile>& file = db->files[file_id.id]; optional<QueryFile>& file = db->files[file_id.id];
if (file) { if (file) {
*path = file->def.path; *path = file->def.path;
return lsDocumentUri::FromPath(*path); return lsDocumentUri::FromPath(*path);
} } else {
else {
*path = ""; *path = "";
return lsDocumentUri::FromPath(""); return lsDocumentUri::FromPath("");
} }
@ -404,25 +426,31 @@ lsDocumentUri GetLsDocumentUri(QueryDatabase* db, QueryFileId file_id) {
optional<QueryFile>& file = db->files[file_id.id]; optional<QueryFile>& file = db->files[file_id.id];
if (file) { if (file) {
return lsDocumentUri::FromPath(file->def.path); return lsDocumentUri::FromPath(file->def.path);
} } else {
else {
return lsDocumentUri::FromPath(""); return lsDocumentUri::FromPath("");
} }
} }
optional<lsLocation> GetLsLocation(QueryDatabase* db, WorkingFiles* working_files, const QueryLocation& location) { optional<lsLocation> GetLsLocation(QueryDatabase* db,
WorkingFiles* working_files,
const QueryLocation& location) {
std::string path; std::string path;
lsDocumentUri uri = GetLsDocumentUri(db, location.path, &path); lsDocumentUri uri = GetLsDocumentUri(db, location.path, &path);
optional<lsRange> range = GetLsRange(working_files->GetFileByFilename(path), location.range); optional<lsRange> range =
GetLsRange(working_files->GetFileByFilename(path), location.range);
if (!range) if (!range)
return nullopt; return nullopt;
return lsLocation(uri, *range); return lsLocation(uri, *range);
} }
NonElidedVector<lsLocation> GetLsLocations(QueryDatabase* db, WorkingFiles* working_files, const std::vector<QueryLocation>& locations) { NonElidedVector<lsLocation> GetLsLocations(
QueryDatabase* db,
WorkingFiles* working_files,
const std::vector<QueryLocation>& locations) {
std::unordered_set<lsLocation> unique_locations; std::unordered_set<lsLocation> unique_locations;
for (const QueryLocation& query_location : locations) { for (const QueryLocation& query_location : locations) {
optional<lsLocation> location = GetLsLocation(db, working_files, query_location); optional<lsLocation> location =
GetLsLocation(db, working_files, query_location);
if (!location) if (!location)
continue; continue;
unique_locations.insert(*location); unique_locations.insert(*location);
@ -435,75 +463,77 @@ NonElidedVector<lsLocation> GetLsLocations(QueryDatabase* db, WorkingFiles* work
} }
// Returns a symbol. The symbol will have *NOT* have a location assigned. // Returns a symbol. The symbol will have *NOT* have a location assigned.
optional<lsSymbolInformation> GetSymbolInfo(QueryDatabase* db, WorkingFiles* working_files, SymbolIdx symbol) { optional<lsSymbolInformation> GetSymbolInfo(QueryDatabase* db,
WorkingFiles* working_files,
SymbolIdx symbol) {
switch (symbol.kind) { switch (symbol.kind) {
case SymbolKind::File: { case SymbolKind::File: {
optional<QueryFile>& file = db->files[symbol.idx]; optional<QueryFile>& file = db->files[symbol.idx];
if (!file) if (!file)
return nullopt; return nullopt;
lsSymbolInformation info; lsSymbolInformation info;
info.name = file->def.path; info.name = file->def.path;
info.kind = lsSymbolKind::File; info.kind = lsSymbolKind::File;
return info; return info;
}
case SymbolKind::Type: {
optional<QueryType>& type = db->types[symbol.idx];
if (!type)
return nullopt;
lsSymbolInformation info;
info.name = type->def.short_name;
if (type->def.detailed_name != type->def.short_name)
info.containerName = type->def.detailed_name;
info.kind = lsSymbolKind::Class;
return info;
}
case SymbolKind::Func: {
optional<QueryFunc>& func = db->funcs[symbol.idx];
if (!func)
return nullopt;
lsSymbolInformation info;
info.name = func->def.short_name;
info.containerName = func->def.detailed_name;
info.kind = lsSymbolKind::Function;
if (func->def.declaring_type.has_value()) {
optional<QueryType>& container = db->types[func->def.declaring_type->id];
if (container)
info.kind = lsSymbolKind::Method;
} }
case SymbolKind::Type: {
optional<QueryType>& type = db->types[symbol.idx];
if (!type)
return nullopt;
return info; lsSymbolInformation info;
} info.name = type->def.short_name;
case SymbolKind::Var: { if (type->def.detailed_name != type->def.short_name)
optional<QueryVar>& var = db->vars[symbol.idx]; info.containerName = type->def.detailed_name;
if (!var) info.kind = lsSymbolKind::Class;
return info;
}
case SymbolKind::Func: {
optional<QueryFunc>& func = db->funcs[symbol.idx];
if (!func)
return nullopt;
lsSymbolInformation info;
info.name = func->def.short_name;
info.containerName = func->def.detailed_name;
info.kind = lsSymbolKind::Function;
if (func->def.declaring_type.has_value()) {
optional<QueryType>& container =
db->types[func->def.declaring_type->id];
if (container)
info.kind = lsSymbolKind::Method;
}
return info;
}
case SymbolKind::Var: {
optional<QueryVar>& var = db->vars[symbol.idx];
if (!var)
return nullopt;
lsSymbolInformation info;
info.name += var->def.short_name;
info.containerName = var->def.detailed_name;
info.kind = lsSymbolKind::Variable;
return info;
}
case SymbolKind::Invalid: {
return nullopt; return nullopt;
}
lsSymbolInformation info;
info.name += var->def.short_name;
info.containerName = var->def.detailed_name;
info.kind = lsSymbolKind::Variable;
return info;
}
case SymbolKind::Invalid: {
return nullopt;
}
}; };
return nullopt; return nullopt;
} }
void AddCodeLens( void AddCodeLens(const char* singular,
const char* singular, const char* plural,
const char* plural, CommonCodeLensParams* common,
CommonCodeLensParams* common, QueryLocation loc,
QueryLocation loc, const std::vector<QueryLocation>& uses,
const std::vector<QueryLocation>& uses, optional<QueryLocation> excluded,
optional<QueryLocation> excluded, bool force_display) {
bool force_display) {
TCodeLens code_lens; TCodeLens code_lens;
optional<lsRange> range = GetLsRange(common->working_file, loc.range); optional<lsRange> range = GetLsRange(common->working_file, loc.range);
if (!range) if (!range)
@ -519,13 +549,14 @@ void AddCodeLens(
for (const QueryLocation& use : uses) { for (const QueryLocation& use : uses) {
if (excluded == use) if (excluded == use)
continue; continue;
optional<lsLocation> location = GetLsLocation(common->db, common->working_files, use); optional<lsLocation> location =
GetLsLocation(common->db, common->working_files, use);
if (!location) if (!location)
continue; continue;
unique_uses.insert(*location); unique_uses.insert(*location);
} }
code_lens.command->arguments.locations.assign(unique_uses.begin(), code_lens.command->arguments.locations.assign(unique_uses.begin(),
unique_uses.end()); unique_uses.end());
// User visible label // User visible label
size_t num_usages = unique_uses.size(); size_t num_usages = unique_uses.size();
@ -539,11 +570,15 @@ void AddCodeLens(
common->result->push_back(code_lens); common->result->push_back(code_lens);
} }
lsWorkspaceEdit BuildWorkspaceEdit(QueryDatabase* db, WorkingFiles* working_files, const std::vector<QueryLocation>& locations, const std::string& new_text) { lsWorkspaceEdit BuildWorkspaceEdit(QueryDatabase* db,
WorkingFiles* working_files,
const std::vector<QueryLocation>& locations,
const std::string& new_text) {
std::unordered_map<QueryFileId, lsTextDocumentEdit> path_to_edit; std::unordered_map<QueryFileId, lsTextDocumentEdit> path_to_edit;
for (auto& location : locations) { for (auto& location : locations) {
optional<lsLocation> ls_location = GetLsLocation(db, working_files, location); optional<lsLocation> ls_location =
GetLsLocation(db, working_files, location);
if (!ls_location) if (!ls_location)
continue; continue;
@ -555,11 +590,13 @@ lsWorkspaceEdit BuildWorkspaceEdit(QueryDatabase* db, WorkingFiles* working_file
continue; continue;
const std::string& path = file->def.path; const std::string& path = file->def.path;
path_to_edit[location.path].textDocument.uri = lsDocumentUri::FromPath(path); path_to_edit[location.path].textDocument.uri =
lsDocumentUri::FromPath(path);
WorkingFile* working_file = working_files->GetFileByFilename(path); WorkingFile* working_file = working_files->GetFileByFilename(path);
if (working_file) if (working_file)
path_to_edit[location.path].textDocument.version = working_file->version; path_to_edit[location.path].textDocument.version =
working_file->version;
} }
lsTextEdit edit; lsTextEdit edit;
@ -572,21 +609,23 @@ lsWorkspaceEdit BuildWorkspaceEdit(QueryDatabase* db, WorkingFiles* working_file
edits.push_back(edit); edits.push_back(edit);
} }
lsWorkspaceEdit edit; lsWorkspaceEdit edit;
for (const auto& changes : path_to_edit) for (const auto& changes : path_to_edit)
edit.documentChanges.push_back(changes.second); edit.documentChanges.push_back(changes.second);
return edit; return edit;
} }
std::vector<SymbolRef> FindSymbolsAtLocation(WorkingFile* working_file, QueryFile* file, lsPosition position) { std::vector<SymbolRef> FindSymbolsAtLocation(WorkingFile* working_file,
QueryFile* file,
lsPosition position) {
std::vector<SymbolRef> symbols; std::vector<SymbolRef> symbols;
symbols.reserve(1); symbols.reserve(1);
int target_line = position.line + 1; int target_line = position.line + 1;
int target_column = position.character + 1; int target_column = position.character + 1;
if (working_file) { if (working_file) {
optional<int> index_line = working_file->GetIndexLineFromBufferLine(target_line); optional<int> index_line =
working_file->GetIndexLineFromBufferLine(target_line);
if (index_line) if (index_line)
target_line = *index_line; target_line = *index_line;
} }
@ -602,20 +641,25 @@ std::vector<SymbolRef> FindSymbolsAtLocation(WorkingFile* working_file, QueryFil
// //
// Order functions before other types, which makes goto definition work // Order functions before other types, which makes goto definition work
// better on constructors. // better on constructors.
std::sort(symbols.begin(), symbols.end(), [](const SymbolRef& a, const SymbolRef& b) { std::sort(symbols.begin(), symbols.end(),
int a_size = ComputeRangeSize(a.loc.range); [](const SymbolRef& a, const SymbolRef& b) {
int b_size = ComputeRangeSize(b.loc.range); int a_size = ComputeRangeSize(a.loc.range);
int b_size = ComputeRangeSize(b.loc.range);
if (a_size == b_size) if (a_size == b_size)
return a.idx.kind != b.idx.kind && a.idx.kind == SymbolKind::Func; return a.idx.kind != b.idx.kind &&
a.idx.kind == SymbolKind::Func;
return a_size < b_size; return a_size < b_size;
}); });
return symbols; return symbols;
} }
NonElidedVector<Out_CqueryTypeHierarchyTree::TypeEntry> BuildParentInheritanceHierarchyForType(QueryDatabase* db, WorkingFiles* working_files, QueryTypeId root) { NonElidedVector<Out_CqueryTypeHierarchyTree::TypeEntry>
BuildParentInheritanceHierarchyForType(QueryDatabase* db,
WorkingFiles* working_files,
QueryTypeId root) {
optional<QueryType>& root_type = db->types[root.id]; optional<QueryType>& root_type = db->types[root.id];
if (!root_type) if (!root_type)
return {}; return {};
@ -631,8 +675,10 @@ NonElidedVector<Out_CqueryTypeHierarchyTree::TypeEntry> BuildParentInheritanceHi
Out_CqueryTypeHierarchyTree::TypeEntry parent_entry; Out_CqueryTypeHierarchyTree::TypeEntry parent_entry;
parent_entry.name = parent_type->def.detailed_name; parent_entry.name = parent_type->def.detailed_name;
if (parent_type->def.definition_spelling) if (parent_type->def.definition_spelling)
parent_entry.location = GetLsLocation(db, working_files, *parent_type->def.definition_spelling); parent_entry.location = GetLsLocation(
parent_entry.children = BuildParentInheritanceHierarchyForType(db, working_files, parent_id); db, working_files, *parent_type->def.definition_spelling);
parent_entry.children =
BuildParentInheritanceHierarchyForType(db, working_files, parent_id);
parent_entries.push_back(parent_entry); parent_entries.push_back(parent_entry);
} }
@ -640,8 +686,10 @@ NonElidedVector<Out_CqueryTypeHierarchyTree::TypeEntry> BuildParentInheritanceHi
return parent_entries; return parent_entries;
} }
optional<Out_CqueryTypeHierarchyTree::TypeEntry>
optional<Out_CqueryTypeHierarchyTree::TypeEntry> BuildInheritanceHierarchyForType(QueryDatabase* db, WorkingFiles* working_files, QueryTypeId root_id) { BuildInheritanceHierarchyForType(QueryDatabase* db,
WorkingFiles* working_files,
QueryTypeId root_id) {
optional<QueryType>& root_type = db->types[root_id.id]; optional<QueryType>& root_type = db->types[root_id.id];
if (!root_type) if (!root_type)
return nullopt; return nullopt;
@ -651,7 +699,8 @@ optional<Out_CqueryTypeHierarchyTree::TypeEntry> BuildInheritanceHierarchyForTyp
// Name and location. // Name and location.
entry.name = root_type->def.detailed_name; entry.name = root_type->def.detailed_name;
if (root_type->def.definition_spelling) if (root_type->def.definition_spelling)
entry.location = GetLsLocation(db, working_files, *root_type->def.definition_spelling); entry.location =
GetLsLocation(db, working_files, *root_type->def.definition_spelling);
entry.children.reserve(root_type->derived.size()); entry.children.reserve(root_type->derived.size());
@ -659,13 +708,15 @@ optional<Out_CqueryTypeHierarchyTree::TypeEntry> BuildInheritanceHierarchyForTyp
Out_CqueryTypeHierarchyTree::TypeEntry base; Out_CqueryTypeHierarchyTree::TypeEntry base;
base.name = "[[Base]]"; base.name = "[[Base]]";
base.location = entry.location; base.location = entry.location;
base.children = BuildParentInheritanceHierarchyForType(db, working_files, root_id); base.children =
BuildParentInheritanceHierarchyForType(db, working_files, root_id);
if (!base.children.empty()) if (!base.children.empty())
entry.children.push_back(base); entry.children.push_back(base);
// Add derived. // Add derived.
for (QueryTypeId derived : root_type->derived) { for (QueryTypeId derived : root_type->derived) {
auto derived_entry = BuildInheritanceHierarchyForType(db, working_files, derived); auto derived_entry =
BuildInheritanceHierarchyForType(db, working_files, derived);
if (derived_entry) if (derived_entry)
entry.children.push_back(*derived_entry); entry.children.push_back(*derived_entry);
} }
@ -673,8 +724,10 @@ optional<Out_CqueryTypeHierarchyTree::TypeEntry> BuildInheritanceHierarchyForTyp
return entry; return entry;
} }
NonElidedVector<Out_CqueryTypeHierarchyTree::TypeEntry>
NonElidedVector<Out_CqueryTypeHierarchyTree::TypeEntry> BuildParentInheritanceHierarchyForFunc(QueryDatabase* db, WorkingFiles* working_files, QueryFuncId root) { BuildParentInheritanceHierarchyForFunc(QueryDatabase* db,
WorkingFiles* working_files,
QueryFuncId root) {
optional<QueryFunc>& root_func = db->funcs[root.id]; optional<QueryFunc>& root_func = db->funcs[root.id];
if (!root_func || !root_func->def.base) if (!root_func || !root_func->def.base)
return {}; return {};
@ -686,16 +739,20 @@ NonElidedVector<Out_CqueryTypeHierarchyTree::TypeEntry> BuildParentInheritanceHi
Out_CqueryTypeHierarchyTree::TypeEntry parent_entry; Out_CqueryTypeHierarchyTree::TypeEntry parent_entry;
parent_entry.name = parent_func->def.detailed_name; parent_entry.name = parent_func->def.detailed_name;
if (parent_func->def.definition_spelling) if (parent_func->def.definition_spelling)
parent_entry.location = GetLsLocation(db, working_files, *parent_func->def.definition_spelling); parent_entry.location =
parent_entry.children = BuildParentInheritanceHierarchyForFunc(db, working_files, *root_func->def.base); GetLsLocation(db, working_files, *parent_func->def.definition_spelling);
parent_entry.children = BuildParentInheritanceHierarchyForFunc(
db, working_files, *root_func->def.base);
NonElidedVector<Out_CqueryTypeHierarchyTree::TypeEntry> entries; NonElidedVector<Out_CqueryTypeHierarchyTree::TypeEntry> entries;
entries.push_back(parent_entry); entries.push_back(parent_entry);
return entries; return entries;
} }
optional<Out_CqueryTypeHierarchyTree::TypeEntry>
optional<Out_CqueryTypeHierarchyTree::TypeEntry> BuildInheritanceHierarchyForFunc(QueryDatabase* db, WorkingFiles* working_files, QueryFuncId root_id) { BuildInheritanceHierarchyForFunc(QueryDatabase* db,
WorkingFiles* working_files,
QueryFuncId root_id) {
optional<QueryFunc>& root_func = db->funcs[root_id.id]; optional<QueryFunc>& root_func = db->funcs[root_id.id];
if (!root_func) if (!root_func)
return nullopt; return nullopt;
@ -705,7 +762,8 @@ optional<Out_CqueryTypeHierarchyTree::TypeEntry> BuildInheritanceHierarchyForFun
// Name and location. // Name and location.
entry.name = root_func->def.detailed_name; entry.name = root_func->def.detailed_name;
if (root_func->def.definition_spelling) if (root_func->def.definition_spelling)
entry.location = GetLsLocation(db, working_files, *root_func->def.definition_spelling); entry.location =
GetLsLocation(db, working_files, *root_func->def.definition_spelling);
entry.children.reserve(root_func->derived.size()); entry.children.reserve(root_func->derived.size());
@ -713,13 +771,15 @@ optional<Out_CqueryTypeHierarchyTree::TypeEntry> BuildInheritanceHierarchyForFun
Out_CqueryTypeHierarchyTree::TypeEntry base; Out_CqueryTypeHierarchyTree::TypeEntry base;
base.name = "[[Base]]"; base.name = "[[Base]]";
base.location = entry.location; base.location = entry.location;
base.children = BuildParentInheritanceHierarchyForFunc(db, working_files, root_id); base.children =
BuildParentInheritanceHierarchyForFunc(db, working_files, root_id);
if (!base.children.empty()) if (!base.children.empty())
entry.children.push_back(base); entry.children.push_back(base);
// Add derived. // Add derived.
for (QueryFuncId derived : root_func->derived) { for (QueryFuncId derived : root_func->derived) {
auto derived_entry = BuildInheritanceHierarchyForFunc(db, working_files, derived); auto derived_entry =
BuildInheritanceHierarchyForFunc(db, working_files, derived);
if (derived_entry) if (derived_entry)
entry.children.push_back(*derived_entry); entry.children.push_back(*derived_entry);
} }
@ -727,13 +787,17 @@ optional<Out_CqueryTypeHierarchyTree::TypeEntry> BuildInheritanceHierarchyForFun
return entry; return entry;
} }
NonElidedVector<Out_CqueryCallTree::CallEntry> BuildInitialCallTree(QueryDatabase* db, WorkingFiles* working_files, QueryFuncId root) { NonElidedVector<Out_CqueryCallTree::CallEntry> BuildInitialCallTree(
QueryDatabase* db,
WorkingFiles* working_files,
QueryFuncId root) {
optional<QueryFunc>& root_func = db->funcs[root.id]; optional<QueryFunc>& root_func = db->funcs[root.id];
if (!root_func) if (!root_func)
return {}; return {};
if (!root_func->def.definition_spelling) if (!root_func->def.definition_spelling)
return {}; return {};
optional<lsLocation> def_loc = GetLsLocation(db, working_files, *root_func->def.definition_spelling); optional<lsLocation> def_loc =
GetLsLocation(db, working_files, *root_func->def.definition_spelling);
if (!def_loc) if (!def_loc)
return {}; return {};
@ -747,12 +811,17 @@ NonElidedVector<Out_CqueryCallTree::CallEntry> BuildInitialCallTree(QueryDatabas
return result; return result;
} }
NonElidedVector<Out_CqueryCallTree::CallEntry> BuildExpandCallTree(QueryDatabase* db, WorkingFiles* working_files, QueryFuncId root) { NonElidedVector<Out_CqueryCallTree::CallEntry> BuildExpandCallTree(
QueryDatabase* db,
WorkingFiles* working_files,
QueryFuncId root) {
optional<QueryFunc>& root_func = db->funcs[root.id]; optional<QueryFunc>& root_func = db->funcs[root.id];
if (!root_func) if (!root_func)
return {}; return {};
auto format_location = [&](const lsLocation& location, optional<QueryTypeId> declaring_type) -> std::string { auto format_location =
[&](const lsLocation& location,
optional<QueryTypeId> declaring_type) -> std::string {
std::string base; std::string base;
if (declaring_type) { if (declaring_type) {
@ -774,8 +843,10 @@ NonElidedVector<Out_CqueryCallTree::CallEntry> BuildExpandCallTree(QueryDatabase
NonElidedVector<Out_CqueryCallTree::CallEntry> result; NonElidedVector<Out_CqueryCallTree::CallEntry> result;
std::unordered_set<QueryLocation> seen_locations; std::unordered_set<QueryLocation> seen_locations;
auto handle_caller = [&](QueryFuncRef caller, Out_CqueryCallTree::CallType call_type) { auto handle_caller = [&](QueryFuncRef caller,
optional<lsLocation> call_location = GetLsLocation(db, working_files, caller.loc); Out_CqueryCallTree::CallType call_type) {
optional<lsLocation> call_location =
GetLsLocation(db, working_files, caller.loc);
if (!call_location) if (!call_location)
return; return;
@ -803,14 +874,15 @@ NonElidedVector<Out_CqueryCallTree::CallEntry> BuildExpandCallTree(QueryDatabase
return; return;
Out_CqueryCallTree::CallEntry call_entry; Out_CqueryCallTree::CallEntry call_entry;
call_entry.name = call_func->def.short_name + " (" + format_location(*call_location, call_func->def.declaring_type) + ")"; call_entry.name =
call_func->def.short_name + " (" +
format_location(*call_location, call_func->def.declaring_type) + ")";
call_entry.usr = call_func->def.usr; call_entry.usr = call_func->def.usr;
call_entry.location = *call_location; call_entry.location = *call_location;
call_entry.hasCallers = HasCallersOnSelfOrBaseOrDerived(db, *call_func); call_entry.hasCallers = HasCallersOnSelfOrBaseOrDerived(db, *call_func);
call_entry.callType = call_type; call_entry.callType = call_type;
result.push_back(call_entry); result.push_back(call_entry);
} } else {
else {
// TODO: See if we can do a better job here. Need more information from // TODO: See if we can do a better job here. Need more information from
// the indexer. // the indexer.
Out_CqueryCallTree::CallEntry call_entry; Out_CqueryCallTree::CallEntry call_entry;
@ -823,9 +895,12 @@ NonElidedVector<Out_CqueryCallTree::CallEntry> BuildExpandCallTree(QueryDatabase
} }
}; };
std::vector<QueryFuncRef> base_callers = GetCallersForAllBaseFunctions(db, *root_func); std::vector<QueryFuncRef> base_callers =
std::vector<QueryFuncRef> derived_callers = GetCallersForAllDerivedFunctions(db, *root_func); GetCallersForAllBaseFunctions(db, *root_func);
result.reserve(root_func->callers.size() + base_callers.size() + derived_callers.size()); std::vector<QueryFuncRef> derived_callers =
GetCallersForAllDerivedFunctions(db, *root_func);
result.reserve(root_func->callers.size() + base_callers.size() +
derived_callers.size());
for (QueryFuncRef caller : root_func->callers) for (QueryFuncRef caller : root_func->callers)
handle_caller(caller, Out_CqueryCallTree::CallType::Direct); handle_caller(caller, Out_CqueryCallTree::CallType::Direct);

View File

@ -7,31 +7,59 @@
#include <optional.h> #include <optional.h>
optional<QueryLocation> GetDefinitionSpellingOfSymbol(QueryDatabase* db, const QueryTypeId& id); optional<QueryLocation> GetDefinitionSpellingOfSymbol(QueryDatabase* db,
optional<QueryLocation> GetDefinitionSpellingOfSymbol(QueryDatabase* db, const QueryFuncId& id); const QueryTypeId& id);
optional<QueryLocation> GetDefinitionSpellingOfSymbol(QueryDatabase* db, const QueryVarId& id); optional<QueryLocation> GetDefinitionSpellingOfSymbol(QueryDatabase* db,
optional<QueryLocation> GetDefinitionSpellingOfSymbol(QueryDatabase* db, const SymbolIdx& symbol); const QueryFuncId& id);
optional<QueryLocation> GetDefinitionExtentOfSymbol(QueryDatabase* db, const SymbolIdx& symbol); optional<QueryLocation> GetDefinitionSpellingOfSymbol(QueryDatabase* db,
const QueryVarId& id);
optional<QueryLocation> GetDefinitionSpellingOfSymbol(QueryDatabase* db,
const SymbolIdx& symbol);
optional<QueryLocation> GetDefinitionExtentOfSymbol(QueryDatabase* db,
const SymbolIdx& symbol);
std::string GetHoverForSymbol(QueryDatabase* db, const SymbolIdx& symbol); std::string GetHoverForSymbol(QueryDatabase* db, const SymbolIdx& symbol);
optional<QueryFileId> GetDeclarationFileForSymbol(QueryDatabase* db, const SymbolIdx& symbol); optional<QueryFileId> GetDeclarationFileForSymbol(QueryDatabase* db,
std::vector<QueryLocation> ToQueryLocation(QueryDatabase* db, const std::vector<QueryFuncRef>& refs); const SymbolIdx& symbol);
std::vector<QueryLocation> ToQueryLocation(QueryDatabase* db, const std::vector<QueryTypeId>& ids); std::vector<QueryLocation> ToQueryLocation(
std::vector<QueryLocation> ToQueryLocation(QueryDatabase* db, const std::vector<QueryFuncId>& ids); QueryDatabase* db,
std::vector<QueryLocation> ToQueryLocation(QueryDatabase* db, const std::vector<QueryVarId>& ids); const std::vector<QueryFuncRef>& refs);
std::vector<QueryLocation> GetUsesOfSymbol(QueryDatabase* db, const SymbolIdx& symbol); std::vector<QueryLocation> ToQueryLocation(QueryDatabase* db,
std::vector<QueryLocation> GetDeclarationsOfSymbolForGotoDefinition(QueryDatabase* db, const SymbolIdx& symbol); const std::vector<QueryTypeId>& ids);
optional<QueryLocation> GetBaseDefinitionOrDeclarationSpelling(QueryDatabase* db, QueryFunc& func); std::vector<QueryLocation> ToQueryLocation(QueryDatabase* db,
const std::vector<QueryFuncId>& ids);
std::vector<QueryLocation> ToQueryLocation(QueryDatabase* db,
const std::vector<QueryVarId>& ids);
std::vector<QueryLocation> GetUsesOfSymbol(QueryDatabase* db,
const SymbolIdx& symbol);
std::vector<QueryLocation> GetDeclarationsOfSymbolForGotoDefinition(
QueryDatabase* db,
const SymbolIdx& symbol);
optional<QueryLocation> GetBaseDefinitionOrDeclarationSpelling(
QueryDatabase* db,
QueryFunc& func);
bool HasCallersOnSelfOrBaseOrDerived(QueryDatabase* db, QueryFunc& root); bool HasCallersOnSelfOrBaseOrDerived(QueryDatabase* db, QueryFunc& root);
std::vector<QueryFuncRef> GetCallersForAllBaseFunctions(QueryDatabase* db, QueryFunc& root); std::vector<QueryFuncRef> GetCallersForAllBaseFunctions(QueryDatabase* db,
std::vector<QueryFuncRef> GetCallersForAllDerivedFunctions(QueryDatabase* db, QueryFunc& root); QueryFunc& root);
optional<lsPosition> GetLsPosition(WorkingFile* working_file, const Position& position); std::vector<QueryFuncRef> GetCallersForAllDerivedFunctions(QueryDatabase* db,
QueryFunc& root);
optional<lsPosition> GetLsPosition(WorkingFile* working_file,
const Position& position);
optional<lsRange> GetLsRange(WorkingFile* working_file, const Range& location); optional<lsRange> GetLsRange(WorkingFile* working_file, const Range& location);
lsDocumentUri GetLsDocumentUri(QueryDatabase* db, QueryFileId file_id, std::string* path); lsDocumentUri GetLsDocumentUri(QueryDatabase* db,
QueryFileId file_id,
std::string* path);
lsDocumentUri GetLsDocumentUri(QueryDatabase* db, QueryFileId file_id); lsDocumentUri GetLsDocumentUri(QueryDatabase* db, QueryFileId file_id);
optional<lsLocation> GetLsLocation(QueryDatabase* db, WorkingFiles* working_files, const QueryLocation& location); optional<lsLocation> GetLsLocation(QueryDatabase* db,
NonElidedVector<lsLocation> GetLsLocations(QueryDatabase* db, WorkingFiles* working_files, const std::vector<QueryLocation>& locations); WorkingFiles* working_files,
const QueryLocation& location);
NonElidedVector<lsLocation> GetLsLocations(
QueryDatabase* db,
WorkingFiles* working_files,
const std::vector<QueryLocation>& locations);
// Returns a symbol. The symbol will have *NOT* have a location assigned. // Returns a symbol. The symbol will have *NOT* have a location assigned.
optional<lsSymbolInformation> GetSymbolInfo(QueryDatabase* db, WorkingFiles* working_files, SymbolIdx symbol); optional<lsSymbolInformation> GetSymbolInfo(QueryDatabase* db,
WorkingFiles* working_files,
SymbolIdx symbol);
struct CommonCodeLensParams { struct CommonCodeLensParams {
std::vector<TCodeLens>* result; std::vector<TCodeLens>* result;
@ -40,21 +68,43 @@ struct CommonCodeLensParams {
WorkingFile* working_file; WorkingFile* working_file;
}; };
void AddCodeLens( void AddCodeLens(const char* singular,
const char* singular, const char* plural,
const char* plural, CommonCodeLensParams* common,
CommonCodeLensParams* common, QueryLocation loc,
QueryLocation loc, const std::vector<QueryLocation>& uses,
const std::vector<QueryLocation>& uses, optional<QueryLocation> excluded,
optional<QueryLocation> excluded, bool force_display);
bool force_display);
lsWorkspaceEdit BuildWorkspaceEdit(QueryDatabase* db, WorkingFiles* working_files, const std::vector<QueryLocation>& locations, const std::string& new_text); lsWorkspaceEdit BuildWorkspaceEdit(QueryDatabase* db,
WorkingFiles* working_files,
const std::vector<QueryLocation>& locations,
const std::string& new_text);
std::vector<SymbolRef> FindSymbolsAtLocation(WorkingFile* working_file, QueryFile* file, lsPosition position); std::vector<SymbolRef> FindSymbolsAtLocation(WorkingFile* working_file,
NonElidedVector<Out_CqueryTypeHierarchyTree::TypeEntry> BuildParentInheritanceHierarchyForType(QueryDatabase* db, WorkingFiles* working_files, QueryTypeId root); QueryFile* file,
optional<Out_CqueryTypeHierarchyTree::TypeEntry> BuildInheritanceHierarchyForType(QueryDatabase* db, WorkingFiles* working_files, QueryTypeId root_id); lsPosition position);
NonElidedVector<Out_CqueryTypeHierarchyTree::TypeEntry> BuildParentInheritanceHierarchyForFunc(QueryDatabase* db, WorkingFiles* working_files, QueryFuncId root); NonElidedVector<Out_CqueryTypeHierarchyTree::TypeEntry>
optional<Out_CqueryTypeHierarchyTree::TypeEntry> BuildInheritanceHierarchyForFunc(QueryDatabase* db, WorkingFiles* working_files, QueryFuncId root_id); BuildParentInheritanceHierarchyForType(QueryDatabase* db,
NonElidedVector<Out_CqueryCallTree::CallEntry> BuildInitialCallTree(QueryDatabase* db, WorkingFiles* working_files, QueryFuncId root); WorkingFiles* working_files,
NonElidedVector<Out_CqueryCallTree::CallEntry> BuildExpandCallTree(QueryDatabase* db, WorkingFiles* working_files, QueryFuncId root); QueryTypeId root);
optional<Out_CqueryTypeHierarchyTree::TypeEntry>
BuildInheritanceHierarchyForType(QueryDatabase* db,
WorkingFiles* working_files,
QueryTypeId root_id);
NonElidedVector<Out_CqueryTypeHierarchyTree::TypeEntry>
BuildParentInheritanceHierarchyForFunc(QueryDatabase* db,
WorkingFiles* working_files,
QueryFuncId root);
optional<Out_CqueryTypeHierarchyTree::TypeEntry>
BuildInheritanceHierarchyForFunc(QueryDatabase* db,
WorkingFiles* working_files,
QueryFuncId root_id);
NonElidedVector<Out_CqueryCallTree::CallEntry> BuildInitialCallTree(
QueryDatabase* db,
WorkingFiles* working_files,
QueryFuncId root);
NonElidedVector<Out_CqueryCallTree::CallEntry> BuildExpandCallTree(
QueryDatabase* db,
WorkingFiles* working_files,
QueryFuncId root);

View File

@ -49,7 +49,6 @@ void Reflect(Writer& visitor, std::string& value) {
visitor.String(value.c_str(), (rapidjson::SizeType)value.size()); visitor.String(value.c_str(), (rapidjson::SizeType)value.size());
} }
// ReflectMember // ReflectMember
void ReflectMember(Writer& visitor, const char* name, std::string& value) { void ReflectMember(Writer& visitor, const char* name, std::string& value) {
if (value.empty()) if (value.empty())
@ -58,20 +57,9 @@ void ReflectMember(Writer& visitor, const char* name, std::string& value) {
Reflect(visitor, value); Reflect(visitor, value);
} }
// TODO: Move this to indexer.cc // TODO: Move this to indexer.cc
template<typename TVisitor> template <typename TVisitor>
void Reflect(TVisitor& visitor, IndexType& value) { void Reflect(TVisitor& visitor, IndexType& value) {
REFLECT_MEMBER_START(); REFLECT_MEMBER_START();
REFLECT_MEMBER2("id", value.id); REFLECT_MEMBER2("id", value.id);
@ -91,8 +79,7 @@ void Reflect(TVisitor& visitor, IndexType& value) {
REFLECT_MEMBER_END(); REFLECT_MEMBER_END();
} }
template <typename TVisitor>
template<typename TVisitor>
void Reflect(TVisitor& visitor, IndexFunc& value) { void Reflect(TVisitor& visitor, IndexFunc& value) {
REFLECT_MEMBER_START(); REFLECT_MEMBER_START();
REFLECT_MEMBER2("id", value.id); REFLECT_MEMBER2("id", value.id);
@ -111,8 +98,7 @@ void Reflect(TVisitor& visitor, IndexFunc& value) {
REFLECT_MEMBER_END(); REFLECT_MEMBER_END();
} }
template <typename TVisitor>
template<typename TVisitor>
void Reflect(TVisitor& visitor, IndexVar& value) { void Reflect(TVisitor& visitor, IndexVar& value) {
REFLECT_MEMBER_START(); REFLECT_MEMBER_START();
REFLECT_MEMBER2("id", value.id); REFLECT_MEMBER2("id", value.id);
@ -130,7 +116,6 @@ void Reflect(TVisitor& visitor, IndexVar& value) {
REFLECT_MEMBER_END(); REFLECT_MEMBER_END();
} }
// IndexFile // IndexFile
bool ReflectMemberStart(Writer& visitor, IndexFile& value) { bool ReflectMemberStart(Writer& visitor, IndexFile& value) {
auto it = value.id_cache.usr_to_type_id.find(""); auto it = value.id_cache.usr_to_type_id.find("");
@ -143,7 +128,7 @@ bool ReflectMemberStart(Writer& visitor, IndexFile& value) {
DefaultReflectMemberStart(visitor); DefaultReflectMemberStart(visitor);
return true; return true;
} }
template<typename TVisitor> template <typename TVisitor>
void Reflect(TVisitor& visitor, IndexFile& value) { void Reflect(TVisitor& visitor, IndexFile& value) {
REFLECT_MEMBER_START(); REFLECT_MEMBER_START();
if (!gTestOutputMode) { if (!gTestOutputMode) {
@ -161,18 +146,12 @@ void Reflect(TVisitor& visitor, IndexFile& value) {
REFLECT_MEMBER_END(); REFLECT_MEMBER_END();
} }
std::string Serialize(IndexFile& file) { std::string Serialize(IndexFile& file) {
rapidjson::StringBuffer output; rapidjson::StringBuffer output;
rapidjson::PrettyWriter<rapidjson::StringBuffer> writer(output); rapidjson::PrettyWriter<rapidjson::StringBuffer> writer(output);
//Writer writer(output); // Writer writer(output);
writer.SetFormatOptions( writer.SetFormatOptions(
rapidjson::PrettyFormatOptions::kFormatSingleLineArray); rapidjson::PrettyFormatOptions::kFormatSingleLineArray);
writer.SetIndent(' ', 2); writer.SetIndent(' ', 2);
Reflect(writer, file); Reflect(writer, file);
@ -180,7 +159,9 @@ std::string Serialize(IndexFile& file) {
return output.GetString(); return output.GetString();
} }
std::unique_ptr<IndexFile> Deserialize(std::string path, std::string serialized, optional<int> expected_version) { std::unique_ptr<IndexFile> Deserialize(std::string path,
std::string serialized,
optional<int> expected_version) {
rapidjson::Document reader; rapidjson::Document reader;
reader.Parse(serialized.c_str()); reader.Parse(serialized.c_str());
if (reader.HasParseError()) if (reader.HasParseError())

View File

@ -9,84 +9,63 @@
#include <string> #include <string>
#include <vector> #include <vector>
using std::experimental::optional;
using std::experimental::nullopt; using std::experimental::nullopt;
using std::experimental::optional;
using Reader = rapidjson::GenericValue<rapidjson::UTF8<>>; using Reader = rapidjson::GenericValue<rapidjson::UTF8<>>;
using Writer = rapidjson::Writer<rapidjson::StringBuffer>; using Writer = rapidjson::Writer<rapidjson::StringBuffer>;
struct IndexFile; struct IndexFile;
#define REFLECT_MEMBER_START() \ #define REFLECT_MEMBER_START() \
if (!ReflectMemberStart(visitor, value)) return if (!ReflectMemberStart(visitor, value)) \
#define REFLECT_MEMBER_START1(value) \ return
if (!ReflectMemberStart(visitor, value)) return #define REFLECT_MEMBER_START1(value) \
#define REFLECT_MEMBER_END() \ if (!ReflectMemberStart(visitor, value)) \
ReflectMemberEnd(visitor, value); return
#define REFLECT_MEMBER_END1(value) \ #define REFLECT_MEMBER_END() ReflectMemberEnd(visitor, value);
ReflectMemberEnd(visitor, value); #define REFLECT_MEMBER_END1(value) ReflectMemberEnd(visitor, value);
#define REFLECT_MEMBER(name) \ #define REFLECT_MEMBER(name) ReflectMember(visitor, #name, value.name)
ReflectMember(visitor, #name, value.name) #define REFLECT_MEMBER2(name, value) ReflectMember(visitor, name, value)
#define REFLECT_MEMBER2(name, value) \
ReflectMember(visitor, name, value)
#define MAKE_REFLECT_TYPE_PROXY(type, as_type) \
#define MAKE_REFLECT_TYPE_PROXY(type, as_type) \ template <typename TVisitor> \
template<typename TVisitor> \
void Reflect(TVisitor& visitor, type& value) { \ void Reflect(TVisitor& visitor, type& value) { \
auto value0 = static_cast<as_type>(value); \ auto value0 = static_cast<as_type>(value); \
Reflect(visitor, value0); \ Reflect(visitor, value0); \
value = static_cast<type>(value0); \ value = static_cast<type>(value0); \
} }
#define _MAPPABLE_REFLECT_MEMBER(name) \ #define _MAPPABLE_REFLECT_MEMBER(name) REFLECT_MEMBER(name);
REFLECT_MEMBER(name);
#define MAKE_REFLECT_EMPTY_STRUCT(type, ...) \ #define MAKE_REFLECT_EMPTY_STRUCT(type, ...) \
template<typename TVisitor> \ template <typename TVisitor> \
void Reflect(TVisitor& visitor, type& value) { \ void Reflect(TVisitor& visitor, type& value) { \
REFLECT_MEMBER_START(); \ REFLECT_MEMBER_START(); \
REFLECT_MEMBER_END(); \ REFLECT_MEMBER_END(); \
} }
#define MAKE_REFLECT_STRUCT(type, ...) \ #define MAKE_REFLECT_STRUCT(type, ...) \
template<typename TVisitor> \ template <typename TVisitor> \
void Reflect(TVisitor& visitor, type& value) { \ void Reflect(TVisitor& visitor, type& value) { \
REFLECT_MEMBER_START(); \ REFLECT_MEMBER_START(); \
MACRO_MAP(_MAPPABLE_REFLECT_MEMBER, __VA_ARGS__) \ MACRO_MAP(_MAPPABLE_REFLECT_MEMBER, __VA_ARGS__) \
REFLECT_MEMBER_END(); \ REFLECT_MEMBER_END(); \
} }
#define _MAPPABLE_REFLECT_ARRAY(name) \ #define _MAPPABLE_REFLECT_ARRAY(name) Reflect(visitor, value.name);
Reflect(visitor, value.name);
// Reflects the struct so it is serialized as an array instead of an object. // Reflects the struct so it is serialized as an array instead of an object.
// This currently only supports writers. // This currently only supports writers.
#define MAKE_REFLECT_STRUCT_WRITER_AS_ARRAY(type, ...) \ #define MAKE_REFLECT_STRUCT_WRITER_AS_ARRAY(type, ...) \
inline void Reflect(Writer& visitor, type& value) { \ inline void Reflect(Writer& visitor, type& value) { \
visitor.StartArray(); \ visitor.StartArray(); \
MACRO_MAP(_MAPPABLE_REFLECT_ARRAY, __VA_ARGS__) \ MACRO_MAP(_MAPPABLE_REFLECT_ARRAY, __VA_ARGS__) \
visitor.EndArray(); \ visitor.EndArray(); \
} }
template <typename T>
template<typename T>
struct NonElidedVector : public std::vector<T> {}; struct NonElidedVector : public std::vector<T> {};
// API: // API:
/* /*
template<typename TVisitor, typename T> template<typename TVisitor, typename T>
@ -108,7 +87,6 @@ void ReflectMemberEnd(TVisitor& visitor, T& value) {
} }
*/ */
// int16_t // int16_t
void Reflect(Reader& visitor, int16_t& value); void Reflect(Reader& visitor, int16_t& value);
void Reflect(Writer& visitor, int16_t& value); void Reflect(Writer& visitor, int16_t& value);
@ -128,21 +106,15 @@ void Reflect(Writer& visitor, bool& value);
void Reflect(Reader& visitor, std::string& value); void Reflect(Reader& visitor, std::string& value);
void Reflect(Writer& visitor, std::string& value); void Reflect(Writer& visitor, std::string& value);
// Writer: // Writer:
template<typename T> template <typename T>
void Reflect(Writer& visitor, std::vector<T>& values) { void Reflect(Writer& visitor, std::vector<T>& values) {
visitor.StartArray(); visitor.StartArray();
for (auto& value : values) for (auto& value : values)
Reflect(visitor, value); Reflect(visitor, value);
visitor.EndArray(); visitor.EndArray();
} }
template<typename T> template <typename T>
void Reflect(Writer& visitor, optional<T> value) { void Reflect(Writer& visitor, optional<T> value) {
if (value) if (value)
Reflect(visitor, value.value()); Reflect(visitor, value.value());
@ -150,21 +122,21 @@ void Reflect(Writer& visitor, optional<T> value) {
inline void DefaultReflectMemberStart(Writer& visitor) { inline void DefaultReflectMemberStart(Writer& visitor) {
visitor.StartObject(); visitor.StartObject();
} }
template<typename T> template <typename T>
bool ReflectMemberStart(Writer& visitor, T& value) { bool ReflectMemberStart(Writer& visitor, T& value) {
visitor.StartObject(); visitor.StartObject();
return true; return true;
} }
template<typename T> template <typename T>
void ReflectMemberEnd(Writer& visitor, T& value) { void ReflectMemberEnd(Writer& visitor, T& value) {
visitor.EndObject(); visitor.EndObject();
} }
template<typename T> template <typename T>
void ReflectMember(Writer& visitor, const char* name, T& value) { void ReflectMember(Writer& visitor, const char* name, T& value) {
visitor.Key(name); visitor.Key(name);
Reflect(visitor, value); Reflect(visitor, value);
} }
template<typename T> template <typename T>
void ReflectMember(Writer& visitor, const char* name, std::vector<T>& values) { void ReflectMember(Writer& visitor, const char* name, std::vector<T>& values) {
if (values.empty()) if (values.empty())
return; return;
@ -174,15 +146,17 @@ void ReflectMember(Writer& visitor, const char* name, std::vector<T>& values) {
Reflect(visitor, value); Reflect(visitor, value);
visitor.EndArray(); visitor.EndArray();
} }
template<typename T> template <typename T>
void ReflectMember(Writer& visitor, const char* name, NonElidedVector<T>& values) { void ReflectMember(Writer& visitor,
const char* name,
NonElidedVector<T>& values) {
visitor.Key(name); visitor.Key(name);
visitor.StartArray(); visitor.StartArray();
for (auto& value : values) for (auto& value : values)
Reflect(visitor, value); Reflect(visitor, value);
visitor.EndArray(); visitor.EndArray();
} }
template<typename T> template <typename T>
void ReflectMember(Writer& visitor, const char* name, optional<T>& value) { void ReflectMember(Writer& visitor, const char* name, optional<T>& value) {
if (!value) if (!value)
return; return;
@ -192,7 +166,7 @@ void ReflectMember(Writer& visitor, const char* name, optional<T>& value) {
void ReflectMember(Writer& visitor, const char* name, std::string& value); void ReflectMember(Writer& visitor, const char* name, std::string& value);
// Reader: // Reader:
template<typename T> template <typename T>
void Reflect(Reader& visitor, std::vector<T>& values) { void Reflect(Reader& visitor, std::vector<T>& values) {
for (auto& entry : visitor.GetArray()) { for (auto& entry : visitor.GetArray()) {
T entry_value; T entry_value;
@ -200,20 +174,20 @@ void Reflect(Reader& visitor, std::vector<T>& values) {
values.push_back(entry_value); values.push_back(entry_value);
} }
} }
template<typename T> template <typename T>
void Reflect(Reader& visitor, optional<T>& value) { void Reflect(Reader& visitor, optional<T>& value) {
T real_value; T real_value;
Reflect(visitor, real_value); Reflect(visitor, real_value);
value = real_value; value = real_value;
} }
inline void DefaultReflectMemberStart(Reader& visitor) {} inline void DefaultReflectMemberStart(Reader& visitor) {}
template<typename T> template <typename T>
bool ReflectMemberStart(Reader& visitor, T& value) { bool ReflectMemberStart(Reader& visitor, T& value) {
return true; return true;
} }
template<typename T> template <typename T>
void ReflectMemberEnd(Reader& visitor, T& value) {} void ReflectMemberEnd(Reader& visitor, T& value) {}
template<typename T> template <typename T>
void ReflectMember(Reader& visitor, const char* name, T& value) { void ReflectMember(Reader& visitor, const char* name, T& value) {
auto it = visitor.FindMember(name); auto it = visitor.FindMember(name);
if (it != visitor.MemberEnd()) { if (it != visitor.MemberEnd()) {
@ -223,6 +197,8 @@ void ReflectMember(Reader& visitor, const char* name, T& value) {
} }
std::string Serialize(IndexFile& file); std::string Serialize(IndexFile& file);
std::unique_ptr<IndexFile> Deserialize(std::string path, std::string serialized, optional<int> expected_version); std::unique_ptr<IndexFile> Deserialize(std::string path,
std::string serialized,
optional<int> expected_version);
void SetTestOutputMode(); void SetTestOutputMode();

View File

@ -2,181 +2,181 @@
// See http://stackoverflow.com/a/2029106. // See http://stackoverflow.com/a/2029106.
const char* kStandardLibraryIncludes[177] = { const char* kStandardLibraryIncludes[177] = {
"aio.h", "aio.h",
"algorithm", "algorithm",
"any", "any",
"arpa/inet.h", "arpa/inet.h",
"array", "array",
"assert.h", "assert.h",
"atomic", "atomic",
"bitset", "bitset",
"cassert", "cassert",
"ccomplex", "ccomplex",
"cctype", "cctype",
"cerrno", "cerrno",
"cfenv", "cfenv",
"cfloat", "cfloat",
"chrono", "chrono",
"cinttypes", "cinttypes",
"ciso646", "ciso646",
"climits", "climits",
"clocale", "clocale",
"cmath", "cmath",
"codecvt", "codecvt",
"complex", "complex",
"complex.h", "complex.h",
"condition_variable", "condition_variable",
"cpio.h", "cpio.h",
"csetjmp", "csetjmp",
"csignal", "csignal",
"cstdalign", "cstdalign",
"cstdarg", "cstdarg",
"cstdbool", "cstdbool",
"cstddef", "cstddef",
"cstdint", "cstdint",
"cstdio", "cstdio",
"cstdlib", "cstdlib",
"cstring", "cstring",
"ctgmath", "ctgmath",
"ctime", "ctime",
"ctype.h", "ctype.h",
"cuchar", "cuchar",
"curses.h", "curses.h",
"cwchar", "cwchar",
"cwctype", "cwctype",
"deque", "deque",
"dirent.h", "dirent.h",
"dlfcn.h", "dlfcn.h",
"errno.h", "errno.h",
"exception", "exception",
"execution", "execution",
"fcntl.h", "fcntl.h",
"fenv.h", "fenv.h",
"filesystem", "filesystem",
"float.h", "float.h",
"fmtmsg.h", "fmtmsg.h",
"fnmatch.h", "fnmatch.h",
"forward_list", "forward_list",
"fstream", "fstream",
"ftw.h", "ftw.h",
"functional", "functional",
"future", "future",
"glob.h", "glob.h",
"grp.h", "grp.h",
"iconv.h", "iconv.h",
"initializer_list", "initializer_list",
"inttypes.h", "inttypes.h",
"iomanip", "iomanip",
"ios", "ios",
"iosfwd", "iosfwd",
"iostream", "iostream",
"iso646.h", "iso646.h",
"istream", "istream",
"iterator", "iterator",
"langinfo.h", "langinfo.h",
"libgen.h", "libgen.h",
"limits", "limits",
"limits.h", "limits.h",
"list", "list",
"locale", "locale",
"locale.h", "locale.h",
"map", "map",
"math.h", "math.h",
"memory", "memory",
"memory_resource", "memory_resource",
"monetary.h", "monetary.h",
"mqueue.h", "mqueue.h",
"mutex", "mutex",
"ndbm.h", "ndbm.h",
"net/if.h", "net/if.h",
"netdb.h", "netdb.h",
"netinet/in.h", "netinet/in.h",
"netinet/tcp.h", "netinet/tcp.h",
"new", "new",
"nl_types.h", "nl_types.h",
"numeric", "numeric",
"optional", "optional",
"ostream", "ostream",
"poll.h", "poll.h",
"pthread.h", "pthread.h",
"pwd.h", "pwd.h",
"queue", "queue",
"random", "random",
"ratio", "ratio",
"regex", "regex",
"regex.h", "regex.h",
"sched.h", "sched.h",
"scoped_allocator", "scoped_allocator",
"search.h", "search.h",
"semaphore.h", "semaphore.h",
"set", "set",
"setjmp.h", "setjmp.h",
"shared_mutex", "shared_mutex",
"signal.h", "signal.h",
"spawn.h", "spawn.h",
"sstream", "sstream",
"stack", "stack",
"stdalign.h", "stdalign.h",
"stdarg.h", "stdarg.h",
"stdatomic.h", "stdatomic.h",
"stdbool.h", "stdbool.h",
"stddef.h", "stddef.h",
"stdexcept", "stdexcept",
"stdint.h", "stdint.h",
"stdio.h", "stdio.h",
"stdlib.h", "stdlib.h",
"stdnoreturn.h", "stdnoreturn.h",
"streambuf", "streambuf",
"string", "string",
"string.h", "string.h",
"string_view", "string_view",
"strings.h", "strings.h",
"stropts.h", "stropts.h",
"strstream", "strstream",
"sys/ipc.h", "sys/ipc.h",
"sys/mman.h", "sys/mman.h",
"sys/msg.h", "sys/msg.h",
"sys/resource.h", "sys/resource.h",
"sys/select.h", "sys/select.h",
"sys/sem.h", "sys/sem.h",
"sys/shm.h", "sys/shm.h",
"sys/socket.h", "sys/socket.h",
"sys/stat.h", "sys/stat.h",
"sys/statvfs.h", "sys/statvfs.h",
"sys/time.h", "sys/time.h",
"sys/times.h", "sys/times.h",
"sys/types.h", "sys/types.h",
"sys/uio.h", "sys/uio.h",
"sys/un.h", "sys/un.h",
"sys/utsname.h", "sys/utsname.h",
"sys/wait.h", "sys/wait.h",
"syslog.h", "syslog.h",
"system_error", "system_error",
"tar.h", "tar.h",
"term.h", "term.h",
"termios.h", "termios.h",
"tgmath.h", "tgmath.h",
"thread", "thread",
"threads.h", "threads.h",
"time.h", "time.h",
"trace.h", "trace.h",
"tuple", "tuple",
"type_traits", "type_traits",
"typeindex", "typeindex",
"typeinfo", "typeinfo",
"uchar.h", "uchar.h",
"ulimit.h", "ulimit.h",
"uncntrl.h", "uncntrl.h",
"unistd.h", "unistd.h",
"unordered_map", "unordered_map",
"unordered_set", "unordered_set",
"utility", "utility",
"utime.h", "utime.h",
"utmpx.h", "utmpx.h",
"valarray", "valarray",
"variant", "variant",
"vector", "vector",
"wchar.h", "wchar.h",
"wctype.h", "wctype.h",
"wordexp.h", "wordexp.h",
}; };

View File

@ -14,7 +14,7 @@ std::string ToString(const rapidjson::Document& document) {
rapidjson::StringBuffer buffer; rapidjson::StringBuffer buffer;
rapidjson::PrettyWriter<rapidjson::StringBuffer> writer(buffer); rapidjson::PrettyWriter<rapidjson::StringBuffer> writer(buffer);
writer.SetFormatOptions( writer.SetFormatOptions(
rapidjson::PrettyFormatOptions::kFormatSingleLineArray); rapidjson::PrettyFormatOptions::kFormatSingleLineArray);
writer.SetIndent(' ', 2); writer.SetIndent(' ', 2);
buffer.Clear(); buffer.Clear();
@ -22,16 +22,24 @@ std::string ToString(const rapidjson::Document& document) {
return buffer.GetString(); return buffer.GetString();
} }
void DiffDocuments(std::string path, std::string path_section, rapidjson::Document& expected, rapidjson::Document& actual) { void DiffDocuments(std::string path,
std::string path_section,
rapidjson::Document& expected,
rapidjson::Document& actual) {
std::string joined_actual_output = ToString(actual); std::string joined_actual_output = ToString(actual);
std::vector<std::string> actual_output = SplitString(joined_actual_output, "\n"); std::vector<std::string> actual_output =
SplitString(joined_actual_output, "\n");
std::string joined_expected_output = ToString(expected); std::string joined_expected_output = ToString(expected);
std::vector<std::string> expected_output = SplitString(joined_expected_output, "\n"); std::vector<std::string> expected_output =
SplitString(joined_expected_output, "\n");
std::cout << "[FAILED] " << path << " (section " << path_section << ")" << std::endl; std::cout << "[FAILED] " << path << " (section " << path_section << ")"
std::cout << "Expected output for " << path << " (section " << path_section << "):" << std::endl; << std::endl;
std::cout << "Expected output for " << path << " (section " << path_section
<< "):" << std::endl;
std::cout << joined_expected_output << std::endl; std::cout << joined_expected_output << std::endl;
std::cout << "Actual output for " << path << " (section " << path_section << "):" << std::endl; std::cout << "Actual output for " << path << " (section " << path_section
<< "):" << std::endl;
std::cout << joined_actual_output << std::endl; std::cout << joined_actual_output << std::endl;
std::cout << std::endl; std::cout << std::endl;
@ -66,15 +74,19 @@ void DiffDocuments(std::string path, std::string path_section, rapidjson::Docume
void VerifySerializeToFrom(IndexFile* file) { void VerifySerializeToFrom(IndexFile* file) {
std::string expected = file->ToString(); std::string expected = file->ToString();
std::unique_ptr<IndexFile> result = Deserialize("--.cc", Serialize(*file), nullopt /*expected_version*/); std::unique_ptr<IndexFile> result =
Deserialize("--.cc", Serialize(*file), nullopt /*expected_version*/);
std::string actual = result->ToString(); std::string actual = result->ToString();
if (expected != actual) { if (expected != actual) {
std::cerr << "Serialization failure" << std::endl;; std::cerr << "Serialization failure" << std::endl;
;
assert(false); assert(false);
} }
} }
std::string FindExpectedOutputForFilename(std::string filename, const std::unordered_map<std::string, std::string>& expected) { std::string FindExpectedOutputForFilename(
std::string filename,
const std::unordered_map<std::string, std::string>& expected) {
for (const auto& entry : expected) { for (const auto& entry : expected) {
if (EndsWith(entry.first, filename)) if (EndsWith(entry.first, filename))
return entry.second; return entry.second;
@ -86,7 +98,9 @@ std::string FindExpectedOutputForFilename(std::string filename, const std::unord
return "{}"; return "{}";
} }
IndexFile* FindDbForPathEnding(const std::string& path, const std::vector<std::unique_ptr<IndexFile>>& dbs) { IndexFile* FindDbForPathEnding(
const std::string& path,
const std::vector<std::unique_ptr<IndexFile>>& dbs) {
for (auto& db : dbs) { for (auto& db : dbs) {
if (EndsWith(db->path, path)) if (EndsWith(db->path, path))
return db.get(); return db.get();
@ -101,48 +115,48 @@ void RunTests() {
bool update_all = false; bool update_all = false;
clang::Index index(1, 0); clang::Index index(1, 0);
for (std::string path : GetFilesInFolder("tests", true /*recursive*/, true /*add_folder_to_path*/)) { for (std::string path : GetFilesInFolder("tests", true /*recursive*/,
true /*add_folder_to_path*/)) {
float memory_before = GetProcessMemoryUsedInMb(); float memory_before = GetProcessMemoryUsedInMb();
float memory_after = -1.; float memory_after = -1.;
{ {
//if (path != "tests/templates/specialized_func_definition.cc") continue; // if (path != "tests/templates/specialized_func_definition.cc") continue;
//if (path != "tests/templates/namespace_template_class_template_func_usage_folded_into_one.cc") continue; // if (path !=
//if (path != "tests/multi_file/funky_enum.cc") continue; // "tests/templates/namespace_template_class_template_func_usage_folded_into_one.cc")
//if (path != "tests/multi_file/simple_impl.cc") continue; // continue; if (path != "tests/multi_file/funky_enum.cc") continue; if
//if (path != "tests/inheritance/interface_pure_virtual.cc") continue; // (path != "tests/multi_file/simple_impl.cc") continue; if (path !=
//if (path != "tests/_empty_test.cc") continue; // "tests/inheritance/interface_pure_virtual.cc") continue; if (path !=
//if (path != "tests/declaration_vs_definition/func_associated_function_params.cc") continue; // "tests/_empty_test.cc") continue; if (path !=
// "tests/declaration_vs_definition/func_associated_function_params.cc")
// continue;
//if (path != "tests/templates/template_class_type_usage_folded_into_one.cc") continue; // if (path !=
//path = "C:/Users/jacob/Desktop/superindex/indexer/" + path; // "tests/templates/template_class_type_usage_folded_into_one.cc")
// continue; path = "C:/Users/jacob/Desktop/superindex/indexer/" + path;
Config config; Config config;
FileConsumer::SharedState file_consumer_shared; FileConsumer::SharedState file_consumer_shared;
// Run test. // Run test.
//std::cout << "[START] " << path << std::endl; // std::cout << "[START] " << path << std::endl;
PerformanceImportFile perf; PerformanceImportFile perf;
std::vector<std::unique_ptr<IndexFile>> dbs = Parse( std::vector<std::unique_ptr<IndexFile>> dbs = Parse(
&config, &file_consumer_shared, &config, &file_consumer_shared, path,
path, {"-xc++", "-std=c++11",
{ "-IC:/Users/jacob/Desktop/cquery/third_party/",
"-xc++", "-IC:/Users/jacob/Desktop/cquery/third_party/doctest/",
"-std=c++11", "-IC:/Users/jacob/Desktop/cquery/third_party/rapidjson/include",
"-IC:/Users/jacob/Desktop/cquery/third_party/", "-IC:/Users/jacob/Desktop/cquery/src",
"-IC:/Users/jacob/Desktop/cquery/third_party/doctest/", "-isystemC:/Program Files (x86)/Microsoft Visual "
"-IC:/Users/jacob/Desktop/cquery/third_party/rapidjson/include", "Studio/2017/Community/VC/Tools/MSVC/14.10.25017/include",
"-IC:/Users/jacob/Desktop/cquery/src", "-isystemC:/Program Files (x86)/Windows "
"-isystemC:/Program Files (x86)/Microsoft Visual Studio/2017/Community/VC/Tools/MSVC/14.10.25017/include", "Kits/10/Include/10.0.15063.0/ucrt"},
"-isystemC:/Program Files (x86)/Windows Kits/10/Include/10.0.15063.0/ucrt" {}, &perf, &index, false /*dump_ast*/);
},
{},
&perf,
&index,
false /*dump_ast*/);
// Parse expected output from the test, parse it into JSON document. // Parse expected output from the test, parse it into JSON document.
std::unordered_map<std::string, std::string> all_expected_output = ParseTestExpectation(path); std::unordered_map<std::string, std::string> all_expected_output =
ParseTestExpectation(path);
for (auto& entry : all_expected_output) { for (auto& entry : all_expected_output) {
const std::string& expected_path = entry.first; const std::string& expected_path = entry.first;
const std::string& expected_output = entry.second; const std::string& expected_output = entry.second;
@ -164,12 +178,12 @@ void RunTests() {
if (actual == expected) { if (actual == expected) {
std::cout << "[PASSED] " << path << std::endl; std::cout << "[PASSED] " << path << std::endl;
} } else {
else {
DiffDocuments(path, expected_path, expected, actual); DiffDocuments(path, expected_path, expected, actual);
std::cout << std::endl; std::cout << std::endl;
std::cout << std::endl; std::cout << std::endl;
std::cout << "[Enter to continue - type u to update test, a to update all]"; std::cout
<< "[Enter to continue - type u to update test, a to update all]";
char c = 'u'; char c = 'u';
if (!update_all) { if (!update_all) {
c = (char)std::cin.get(); c = (char)std::cin.get();
@ -180,9 +194,9 @@ void RunTests() {
update_all = true; update_all = true;
if (update_all || c == 'u') { if (update_all || c == 'u') {
UpdateTestExpectation(path, expected_output, ToString(actual) + "\n"); UpdateTestExpectation(path, expected_output,
ToString(actual) + "\n");
} }
} }
} }
@ -190,12 +204,16 @@ void RunTests() {
} }
float memory_cleanup = GetProcessMemoryUsedInMb(); float memory_cleanup = GetProcessMemoryUsedInMb();
std::cerr << "[memory] before=" << memory_before << "mb, after=" << memory_after << "mb, cleanup=" << memory_cleanup << "mb" << std::endl; std::cerr << "[memory] before=" << memory_before
<< "mb, after=" << memory_after
<< "mb, cleanup=" << memory_cleanup << "mb" << std::endl;
} }
std::cerr << "[final presleep] " << GetProcessMemoryUsedInMb() << "mb" << std::endl; std::cerr << "[final presleep] " << GetProcessMemoryUsedInMb() << "mb"
//std::this_thread::sleep_for(std::chrono::seconds(10)); << std::endl;
//std::cerr << "[final postsleep] " << GetProcessMemoryUsedInMb() << "mb" << std::endl; // std::this_thread::sleep_for(std::chrono::seconds(10));
// std::cerr << "[final postsleep] " << GetProcessMemoryUsedInMb() << "mb" <<
// std::endl;
std::cerr << std::endl; std::cerr << std::endl;
std::cerr << std::endl; std::cerr << std::endl;
std::cerr << std::endl; std::cerr << std::endl;
@ -204,5 +222,5 @@ void RunTests() {
} }
// TODO: ctor/dtor, copy ctor // TODO: ctor/dtor, copy ctor
// TODO: Always pass IndexFile by pointer, ie, search and remove all IndexFile& refs. // TODO: Always pass IndexFile by pointer, ie, search and remove all IndexFile&
// refs.

View File

@ -1,3 +1,3 @@
#pragma once #pragma once
void RunTests(); void RunTests();

View File

@ -1,16 +1,18 @@
#pragma once #pragma once
#include "work_thread.h"
#include <optional.h> #include <optional.h>
#include "work_thread.h"
#include <algorithm> #include <algorithm>
#include <atomic> #include <atomic>
#include <queue>
#include <mutex>
#include <condition_variable> #include <condition_variable>
#include <mutex>
#include <queue>
using std::experimental::optional;
using std::experimental::nullopt; using std::experimental::nullopt;
using std::experimental::optional;
// TODO: cleanup includes. // TODO: cleanup includes.
@ -56,7 +58,7 @@ struct MultiQueueWaiter {
// A threadsafe-queue. http://stackoverflow.com/a/16075550 // A threadsafe-queue. http://stackoverflow.com/a/16075550
template <class T> template <class T>
struct ThreadedQueue : public BaseThreadQueue { struct ThreadedQueue : public BaseThreadQueue {
public: public:
ThreadedQueue() { ThreadedQueue() {
owned_waiter_ = MakeUnique<MultiQueueWaiter>(); owned_waiter_ = MakeUnique<MultiQueueWaiter>();
waiter_ = owned_waiter_.get(); waiter_ = owned_waiter_.get();
@ -111,12 +113,11 @@ public:
// Get the first element from the queue. Blocks until one is available. // Get the first element from the queue. Blocks until one is available.
// Executes |action| with an acquired |mutex_|. // Executes |action| with an acquired |mutex_|.
template<typename TAction> template <typename TAction>
T DequeuePlusAction(TAction action) { T DequeuePlusAction(TAction action) {
std::unique_lock<std::mutex> lock(mutex_); std::unique_lock<std::mutex> lock(mutex_);
waiter_->cv.wait(lock, [&]() { waiter_->cv.wait(lock,
return !priority_.empty() || !queue_.empty(); [&]() { return !priority_.empty() || !queue_.empty(); });
});
if (!priority_.empty()) { if (!priority_.empty()) {
auto val = std::move(priority_.front()); auto val = std::move(priority_.front());
@ -139,7 +140,7 @@ public:
// Get the first element from the queue without blocking. Returns a null // Get the first element from the queue without blocking. Returns a null
// value if the queue is empty. // value if the queue is empty.
template<typename TAction> template <typename TAction>
optional<T> TryDequeuePlusAction(TAction action) { optional<T> TryDequeuePlusAction(TAction action) {
std::lock_guard<std::mutex> lock(mutex_); std::lock_guard<std::mutex> lock(mutex_);
if (priority_.empty() && queue_.empty()) if (priority_.empty() && queue_.empty())

View File

@ -10,12 +10,15 @@ Timer::Timer() {
long long Timer::ElapsedMicroseconds() const { long long Timer::ElapsedMicroseconds() const {
std::chrono::time_point<Clock> end = Clock::now(); std::chrono::time_point<Clock> end = Clock::now();
return std::chrono::duration_cast<std::chrono::microseconds>(end - start).count(); return std::chrono::duration_cast<std::chrono::microseconds>(end - start)
.count();
} }
long long Timer::ElapsedMicrosecondsAndReset() { long long Timer::ElapsedMicrosecondsAndReset() {
std::chrono::time_point<Clock> end = Clock::now(); std::chrono::time_point<Clock> end = Clock::now();
long long microseconds = std::chrono::duration_cast<std::chrono::microseconds>(end - start).count(); long long microseconds =
std::chrono::duration_cast<std::chrono::microseconds>(end - start)
.count();
Reset(); Reset();
return microseconds; return microseconds;
} }
@ -26,11 +29,14 @@ void Timer::Reset() {
void Timer::ResetAndPrint(const std::string& message) { void Timer::ResetAndPrint(const std::string& message) {
std::chrono::time_point<Clock> end = Clock::now(); std::chrono::time_point<Clock> end = Clock::now();
long long elapsed = std::chrono::duration_cast<std::chrono::microseconds>(end - start).count(); long long elapsed =
std::chrono::duration_cast<std::chrono::microseconds>(end - start)
.count();
long long milliseconds = elapsed / 1000; long long milliseconds = elapsed / 1000;
long long remaining = elapsed - milliseconds; long long remaining = elapsed - milliseconds;
LOG_S(INFO) << message << " took " << milliseconds << "." << remaining << "ms"; LOG_S(INFO) << message << " took " << milliseconds << "." << remaining
<< "ms";
Reset(); Reset();
} }

View File

@ -17,12 +17,10 @@ struct TypedBidiMessageQueue {
std::function<std::unique_ptr<TMessage>(Reader& visitor)>; std::function<std::unique_ptr<TMessage>(Reader& visitor)>;
TypedBidiMessageQueue(const std::string& name, size_t buffer_size) TypedBidiMessageQueue(const std::string& name, size_t buffer_size)
: for_server( : for_server(Buffer::CreateSharedBuffer(name + "_fs", buffer_size),
Buffer::CreateSharedBuffer(name + "_fs", buffer_size), false /*buffer_has_data*/),
false /*buffer_has_data*/), for_client(Buffer::CreateSharedBuffer(name + "_fc", buffer_size),
for_client( true /*buffer_has_data*/) {}
Buffer::CreateSharedBuffer(name + "_fc", buffer_size),
true /*buffer_has_data*/) {}
void RegisterId(TId id, void RegisterId(TId id,
const Serializer& serializer, const Serializer& serializer,
@ -42,7 +40,8 @@ struct TypedBidiMessageQueue {
writer.SetIndent(' ', 0); writer.SetIndent(' ', 0);
// Serialize the message. // Serialize the message.
assert(serializers_.find(id) != serializers_.end() && "No registered serializer"); assert(serializers_.find(id) != serializers_.end() &&
"No registered serializer");
const Serializer& serializer = serializers_.find(id)->second; const Serializer& serializer = serializers_.find(id)->second;
serializer(writer, message); serializer(writer, message);

View File

@ -7,25 +7,29 @@
#include <algorithm> #include <algorithm>
#include <cassert> #include <cassert>
#include <cctype> #include <cctype>
#include <fstream>
#include <functional> #include <functional>
#include <iostream> #include <iostream>
#include <fstream>
#include <locale> #include <locale>
#include <sstream> #include <sstream>
#include <unordered_map> #include <unordered_map>
#if !defined(__APPLE__) #if !defined(__APPLE__)
#include <sparsepp/spp_memory.h> #include <sparsepp/spp_memory.h>
#endif #endif
// See http://stackoverflow.com/a/217605 // See http://stackoverflow.com/a/217605
void TrimStart(std::string& s) { void TrimStart(std::string& s) {
s.erase(s.begin(), std::find_if(s.begin(), s.end(), s.erase(s.begin(),
std::not1(std::ptr_fun<int, int>(std::isspace)))); std::find_if(s.begin(), s.end(),
std::not1(std::ptr_fun<int, int>(std::isspace))));
} }
void TrimEnd(std::string& s) { void TrimEnd(std::string& s) {
s.erase(std::find_if(s.rbegin(), s.rend(), s.erase(std::find_if(s.rbegin(), s.rend(),
std::not1(std::ptr_fun<int, int>(std::isspace))).base(), s.end()); std::not1(std::ptr_fun<int, int>(std::isspace)))
.base(),
s.end());
} }
void Trim(std::string& s) { void Trim(std::string& s) {
TrimStart(s); TrimStart(s);
@ -45,20 +49,24 @@ bool StartsWith(const std::string& value, const std::string& start) {
return std::equal(start.begin(), start.end(), value.begin()); return std::equal(start.begin(), start.end(), value.begin());
} }
bool AnyStartsWith(const std::vector<std::string>& values, const std::string& start) { bool AnyStartsWith(const std::vector<std::string>& values,
return std::any_of(std::begin(values), std::end(values), [&start](const std::string& value) { const std::string& start) {
return StartsWith(value, start); return std::any_of(
}); std::begin(values), std::end(values),
[&start](const std::string& value) { return StartsWith(value, start); });
} }
bool EndsWithAny(const std::string& value, const std::vector<std::string>& endings) { bool EndsWithAny(const std::string& value,
return std::any_of(std::begin(endings), std::end(endings), [&value](const std::string& ending) { const std::vector<std::string>& endings) {
return EndsWith(value, ending); return std::any_of(
}); std::begin(endings), std::end(endings),
[&value](const std::string& ending) { return EndsWith(value, ending); });
} }
// See http://stackoverflow.com/a/29752943 // See http://stackoverflow.com/a/29752943
std::string ReplaceAll(const std::string& source, const std::string& from, const std::string& to) { std::string ReplaceAll(const std::string& source,
const std::string& from,
const std::string& to) {
std::string result; std::string result;
result.reserve(source.length()); // avoids a few memory allocations result.reserve(source.length()); // avoids a few memory allocations
@ -77,7 +85,8 @@ std::string ReplaceAll(const std::string& source, const std::string& from, const
return result; return result;
} }
std::vector<std::string> SplitString(const std::string& str, const std::string& delimiter) { std::vector<std::string> SplitString(const std::string& str,
const std::string& delimiter) {
// http://stackoverflow.com/a/13172514 // http://stackoverflow.com/a/13172514
std::vector<std::string> strings; std::vector<std::string> strings;
@ -104,17 +113,20 @@ std::string LowerPathIfCaseInsensitive(const std::string& path) {
} }
static void GetFilesInFolderHelper( static void GetFilesInFolderHelper(
std::string folder, bool recursive, std::string output_prefix, const std::function<void(const std::string&)>& handler) { std::string folder,
bool recursive,
std::string output_prefix,
const std::function<void(const std::string&)>& handler) {
tinydir_dir dir; tinydir_dir dir;
if (tinydir_open(&dir, folder.c_str()) == -1) { if (tinydir_open(&dir, folder.c_str()) == -1) {
//perror("Error opening file"); // perror("Error opening file");
goto bail; goto bail;
} }
while (dir.has_next) { while (dir.has_next) {
tinydir_file file; tinydir_file file;
if (tinydir_readfile(&dir, &file) == -1) { if (tinydir_readfile(&dir, &file) == -1) {
//perror("Error getting file"); // perror("Error getting file");
goto bail; goto bail;
} }
@ -127,10 +139,10 @@ static void GetFilesInFolderHelper(
if (recursive) { if (recursive) {
std::string child_dir = output_prefix + file.name + "/"; std::string child_dir = output_prefix + file.name + "/";
if (!IsSymLink(child_dir)) if (!IsSymLink(child_dir))
GetFilesInFolderHelper(file.path, true /*recursive*/, child_dir, handler); GetFilesInFolderHelper(file.path, true /*recursive*/, child_dir,
handler);
} }
} } else {
else {
handler(output_prefix + file.name); handler(output_prefix + file.name);
} }
} }
@ -145,19 +157,24 @@ bail:
tinydir_close(&dir); tinydir_close(&dir);
} }
std::vector<std::string> GetFilesInFolder(std::string folder, bool recursive, bool add_folder_to_path) { std::vector<std::string> GetFilesInFolder(std::string folder,
bool recursive,
bool add_folder_to_path) {
EnsureEndsInSlash(folder); EnsureEndsInSlash(folder);
std::vector<std::string> result; std::vector<std::string> result;
GetFilesInFolderHelper(folder, recursive, add_folder_to_path ? folder : "", [&result](const std::string& path) { GetFilesInFolderHelper(
result.push_back(path); folder, recursive, add_folder_to_path ? folder : "",
}); [&result](const std::string& path) { result.push_back(path); });
return result; return result;
} }
void GetFilesInFolder(std::string folder,
void GetFilesInFolder(std::string folder, bool recursive, bool add_folder_to_path, const std::function<void(const std::string&)>& handler) { bool recursive,
bool add_folder_to_path,
const std::function<void(const std::string&)>& handler) {
EnsureEndsInSlash(folder); EnsureEndsInSlash(folder);
GetFilesInFolderHelper(folder, recursive, add_folder_to_path ? folder : "", handler); GetFilesInFolderHelper(folder, recursive, add_folder_to_path ? folder : "",
handler);
} }
void EnsureEndsInSlash(std::string& path) { void EnsureEndsInSlash(std::string& path) {
@ -165,38 +182,37 @@ void EnsureEndsInSlash(std::string& path) {
path += '/'; path += '/';
} }
// http://stackoverflow.com/a/6089413 // http://stackoverflow.com/a/6089413
std::istream& SafeGetline(std::istream& is, std::string& t) { std::istream& SafeGetline(std::istream& is, std::string& t) {
t.clear(); t.clear();
// The characters in the stream are read one-by-one using a std::streambuf. // The characters in the stream are read one-by-one using a std::streambuf.
// That is faster than reading them one-by-one using the std::istream. // That is faster than reading them one-by-one using the std::istream.
// Code that uses streambuf this way must be guarded by a sentry object. // Code that uses streambuf this way must be guarded by a sentry object.
// The sentry object performs various tasks, // The sentry object performs various tasks,
// such as thread synchronization and updating the stream state. // such as thread synchronization and updating the stream state.
std::istream::sentry se(is, true); std::istream::sentry se(is, true);
std::streambuf* sb = is.rdbuf(); std::streambuf* sb = is.rdbuf();
for(;;) { for (;;) {
int c = sb->sbumpc(); int c = sb->sbumpc();
switch (c) { switch (c) {
case '\n': case '\n':
return is; return is;
case '\r': case '\r':
if(sb->sgetc() == '\n') if (sb->sgetc() == '\n')
sb->sbumpc(); sb->sbumpc();
return is; return is;
case EOF: case EOF:
// Also handle the case when the last line has no line ending // Also handle the case when the last line has no line ending
if(t.empty()) if (t.empty())
is.setstate(std::ios::eofbit); is.setstate(std::ios::eofbit);
return is; return is;
default: default:
t += (char)c; t += (char)c;
}
} }
}
} }
optional<std::string> ReadContent(const std::string& filename) { optional<std::string> ReadContent(const std::string& filename) {
@ -205,22 +221,22 @@ optional<std::string> ReadContent(const std::string& filename) {
if (!cache.good()) if (!cache.good())
return nullopt; return nullopt;
return std::string( return std::string(std::istreambuf_iterator<char>(cache),
std::istreambuf_iterator<char>(cache), std::istreambuf_iterator<char>());
std::istreambuf_iterator<char>());
} }
std::vector<std::string> ReadLines(std::string filename) { std::vector<std::string> ReadLines(std::string filename) {
std::vector<std::string> result; std::vector<std::string> result;
std::ifstream input(filename); std::ifstream input(filename);
for (std::string line; SafeGetline(input, line); ) for (std::string line; SafeGetline(input, line);)
result.push_back(line); result.push_back(line);
return result; return result;
} }
std::vector<std::string> ToLines(const std::string& content, bool trim_whitespace) { std::vector<std::string> ToLines(const std::string& content,
bool trim_whitespace) {
std::vector<std::string> result; std::vector<std::string> result;
std::istringstream lines(content); std::istringstream lines(content);
@ -235,11 +251,12 @@ std::vector<std::string> ToLines(const std::string& content, bool trim_whitespac
return result; return result;
} }
std::unordered_map<std::string, std::string> ParseTestExpectation(std::string filename) { std::unordered_map<std::string, std::string> ParseTestExpectation(
std::string filename) {
bool in_output = false; bool in_output = false;
#if false #if false
#include "bar.h" #include "bar.h"
void foo(); void foo();
@ -275,8 +292,7 @@ std::unordered_map<std::string, std::string> ParseTestExpectation(std::string fi
active_output_contents = ""; active_output_contents = "";
in_output = true; in_output = true;
} } else if (in_output)
else if (in_output)
active_output_contents += line + "\n"; active_output_contents += line + "\n";
} }
@ -285,11 +301,14 @@ std::unordered_map<std::string, std::string> ParseTestExpectation(std::string fi
return result; return result;
} }
void UpdateTestExpectation(const std::string& filename, const std::string& expectation, const std::string& actual) { void UpdateTestExpectation(const std::string& filename,
const std::string& expectation,
const std::string& actual) {
// Read the entire file into a string. // Read the entire file into a string.
std::ifstream in(filename); std::ifstream in(filename);
std::string str; std::string str;
str.assign(std::istreambuf_iterator<char>(in), std::istreambuf_iterator<char>()); str.assign(std::istreambuf_iterator<char>(in),
std::istreambuf_iterator<char>());
in.close(); in.close();
// Replace expectation // Replace expectation
@ -333,4 +352,3 @@ std::string FormatMicroseconds(long long microseconds) {
return std::to_string(milliseconds) + "." + std::to_string(remaining) + "ms"; return std::to_string(milliseconds) + "." + std::to_string(remaining) + "ms";
} }

View File

@ -10,8 +10,8 @@
#include <unordered_map> #include <unordered_map>
#include <vector> #include <vector>
using std::experimental::optional;
using std::experimental::nullopt; using std::experimental::nullopt;
using std::experimental::optional;
// Trim from start (in place) // Trim from start (in place)
void TrimStart(std::string& s); void TrimStart(std::string& s);
@ -23,16 +23,20 @@ void Trim(std::string& s);
// Returns true if |value| starts/ends with |start| or |ending|. // Returns true if |value| starts/ends with |start| or |ending|.
bool StartsWith(const std::string& value, const std::string& start); bool StartsWith(const std::string& value, const std::string& start);
bool EndsWith(const std::string& value, const std::string& ending); bool EndsWith(const std::string& value, const std::string& ending);
bool AnyStartsWith(const std::vector<std::string>& values, const std::string& start); bool AnyStartsWith(const std::vector<std::string>& values,
bool EndsWithAny(const std::string& value, const std::vector<std::string>& endings); const std::string& start);
bool EndsWithAny(const std::string& value,
const std::vector<std::string>& endings);
std::string ReplaceAll(const std::string& source, const std::string& from, const std::string& to); std::string ReplaceAll(const std::string& source,
const std::string& from,
const std::string& to);
std::vector<std::string> SplitString(const std::string& str, const std::string& delimiter); std::vector<std::string> SplitString(const std::string& str,
const std::string& delimiter);
std::string LowerPathIfCaseInsensitive(const std::string& path); std::string LowerPathIfCaseInsensitive(const std::string& path);
template <typename TValues, typename TMap> template <typename TValues, typename TMap>
std::string StringJoinMap(const TValues& values, const TMap& map) { std::string StringJoinMap(const TValues& values, const TMap& map) {
std::string result; std::string result;
@ -48,56 +52,66 @@ std::string StringJoinMap(const TValues& values, const TMap& map) {
template <typename TValues> template <typename TValues>
std::string StringJoin(const TValues& values) { std::string StringJoin(const TValues& values) {
return StringJoinMap(values, [](const std::string& entry) { return StringJoinMap(values, [](const std::string& entry) { return entry; });
return entry;
});
} }
// Finds all files in the given folder. This is recursive. // Finds all files in the given folder. This is recursive.
std::vector<std::string> GetFilesInFolder(std::string folder, bool recursive, bool add_folder_to_path); std::vector<std::string> GetFilesInFolder(std::string folder,
void GetFilesInFolder(std::string folder, bool recursive, bool add_folder_to_path, const std::function<void(const std::string&)>& handler); bool recursive,
bool add_folder_to_path);
void GetFilesInFolder(std::string folder,
bool recursive,
bool add_folder_to_path,
const std::function<void(const std::string&)>& handler);
// Ensures that |path| ends in a slash. // Ensures that |path| ends in a slash.
void EnsureEndsInSlash(std::string& path); void EnsureEndsInSlash(std::string& path);
optional<std::string> ReadContent(const std::string& filename); optional<std::string> ReadContent(const std::string& filename);
std::vector<std::string> ReadLines(std::string filename); std::vector<std::string> ReadLines(std::string filename);
std::vector<std::string> ToLines(const std::string& content, bool trim_whitespace); std::vector<std::string> ToLines(const std::string& content,
bool trim_whitespace);
std::unordered_map<std::string, std::string> ParseTestExpectation(
std::unordered_map<std::string, std::string> ParseTestExpectation(std::string filename); std::string filename);
void UpdateTestExpectation(const std::string& filename, const std::string& expectation, const std::string& actual); void UpdateTestExpectation(const std::string& filename,
const std::string& expectation,
const std::string& actual);
void Fail(const std::string& message); void Fail(const std::string& message);
void WriteToFile(const std::string& filename, const std::string& content); void WriteToFile(const std::string& filename, const std::string& content);
// note: this implementation does not disable this overload for array types // note: this implementation does not disable this overload for array types
// See http://en.cppreference.com/w/cpp/memory/unique_ptr/make_unique#Possible_Implementatiog // See
template<typename T, typename... Args> // http://en.cppreference.com/w/cpp/memory/unique_ptr/make_unique#Possible_Implementatiog
template <typename T, typename... Args>
std::unique_ptr<T> MakeUnique(Args&&... args) { std::unique_ptr<T> MakeUnique(Args&&... args) {
return std::unique_ptr<T>(new T(std::forward<Args>(args)...)); return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
} }
template<typename T> template <typename T>
void AddRange(std::vector<T>* dest, const std::vector<T>& to_add) { void AddRange(std::vector<T>* dest, const std::vector<T>& to_add) {
for (const T& e : to_add) for (const T& e : to_add)
dest->push_back(e); dest->push_back(e);
} }
template<typename T> template <typename T>
void PushRange(std::queue<T>* dest, const std::vector<T>& to_add) { void PushRange(std::queue<T>* dest, const std::vector<T>& to_add) {
for (const T& e : to_add) for (const T& e : to_add)
dest->push(e); dest->push(e);
} }
template<typename T> template <typename T>
void RemoveRange(std::vector<T>* dest, const std::vector<T>& to_remove) { void RemoveRange(std::vector<T>* dest, const std::vector<T>& to_remove) {
dest->erase(std::remove_if(dest->begin(), dest->end(), [&](const T& t) { dest->erase(std::remove_if(dest->begin(), dest->end(),
// TODO: make to_remove a set? [&](const T& t) {
return std::find(to_remove.begin(), to_remove.end(), t) != to_remove.end(); // TODO: make to_remove a set?
}), dest->end()); return std::find(to_remove.begin(),
to_remove.end(),
t) != to_remove.end();
}),
dest->end());
} }
// http://stackoverflow.com/a/38140932 // http://stackoverflow.com/a/38140932
@ -109,7 +123,7 @@ void RemoveRange(std::vector<T>* dest, const std::vector<T>& to_remove) {
// }; // };
// MAKE_HASHABLE(SomeHashKey, t.key1, t.key2, t.key3) // MAKE_HASHABLE(SomeHashKey, t.key1, t.key2, t.key3)
inline void hash_combine(std::size_t& seed) { } inline void hash_combine(std::size_t& seed) {}
template <typename T, typename... Rest> template <typename T, typename... Rest>
inline void hash_combine(std::size_t& seed, const T& v, Rest... rest) { inline void hash_combine(std::size_t& seed, const T& v, Rest... rest) {
@ -118,25 +132,27 @@ inline void hash_combine(std::size_t& seed, const T& v, Rest... rest) {
hash_combine(seed, rest...); hash_combine(seed, rest...);
} }
#define MAKE_HASHABLE(type, ...) \ #define MAKE_HASHABLE(type, ...) \
namespace std {\ namespace std { \
template<> struct hash<type> {\ template <> \
std::size_t operator()(const type &t) const {\ struct hash<type> { \
std::size_t ret = 0;\ std::size_t operator()(const type& t) const { \
hash_combine(ret, __VA_ARGS__);\ std::size_t ret = 0; \
return ret;\ hash_combine(ret, __VA_ARGS__); \
}\ return ret; \
};\ } \
} }; \
}
#define MAKE_ENUM_HASHABLE(type) \ #define MAKE_ENUM_HASHABLE(type) \
namespace std {\ namespace std { \
template<> struct hash<type> {\ template <> \
std::size_t operator()(const type &t) const {\ struct hash<type> { \
return hash<int>()(static_cast<int>(t));\ std::size_t operator()(const type& t) const { \
}\ return hash<int>()(static_cast<int>(t)); \
};\ } \
} }; \
}
float GetProcessMemoryUsedInMb(); float GetProcessMemoryUsedInMb();

View File

@ -6,9 +6,8 @@ std::atomic<int> WorkThread::num_active_threads;
std::atomic<bool> WorkThread::request_exit_on_idle; std::atomic<bool> WorkThread::request_exit_on_idle;
// static // static
void WorkThread::StartThread( void WorkThread::StartThread(const std::string& thread_name,
const std::string& thread_name, const std::function<Result()>& entry_point) {
const std::function<Result()>& entry_point) {
new std::thread([thread_name, entry_point]() { new std::thread([thread_name, entry_point]() {
SetCurrentThreadName(thread_name); SetCurrentThreadName(thread_name);

View File

@ -9,11 +9,7 @@
// Helper methods for starting threads that do some work. Enables test code to // Helper methods for starting threads that do some work. Enables test code to
// wait for all work to complete. // wait for all work to complete.
struct WorkThread { struct WorkThread {
enum class Result { enum class Result { MoreWork, NoWork, ExitThread };
MoreWork,
NoWork,
ExitThread
};
// The number of active worker threads. // The number of active worker threads.
static std::atomic<int> num_active_threads; static std::atomic<int> num_active_threads;
@ -22,9 +18,8 @@ struct WorkThread {
// Launch a new thread. |entry_point| will be called continously. It should // Launch a new thread. |entry_point| will be called continously. It should
// return true if it there is still known work to be done. // return true if it there is still known work to be done.
static void StartThread( static void StartThread(const std::string& thread_name,
const std::string& thread_name, const std::function<Result()>& entry_point);
const std::function<Result()>& entry_point);
// Static-only class. // Static-only class.
WorkThread() = delete; WorkThread() = delete;

View File

@ -19,8 +19,7 @@ lsPosition GetPositionForOffset(const std::string& content, int offset) {
if (content[i] == '\n') { if (content[i] == '\n') {
result.line += 1; result.line += 1;
result.character = 0; result.character = 0;
} } else {
else {
result.character += 1; result.character += 1;
} }
++i; ++i;
@ -31,9 +30,8 @@ lsPosition GetPositionForOffset(const std::string& content, int offset) {
} // namespace } // namespace
WorkingFile::WorkingFile(const std::string& filename,
const std::string& buffer_content)
WorkingFile::WorkingFile(const std::string& filename, const std::string& buffer_content)
: filename(filename), buffer_content(buffer_content) { : filename(filename), buffer_content(buffer_content) {
OnBufferContentUpdated(); OnBufferContentUpdated();
@ -51,7 +49,7 @@ void WorkingFile::SetIndexContent(const std::string& index_content) {
auto it = index_lines_lookup.find(index_line); auto it = index_lines_lookup.find(index_line);
if (it == index_lines_lookup.end()) if (it == index_lines_lookup.end())
index_lines_lookup[index_line] = { i + 1 }; index_lines_lookup[index_line] = {i + 1};
else else
it->second.push_back(i + 1); it->second.push_back(i + 1);
} }
@ -68,7 +66,7 @@ void WorkingFile::OnBufferContentUpdated() {
auto it = all_buffer_lines_lookup.find(buffer_line); auto it = all_buffer_lines_lookup.find(buffer_line);
if (it == all_buffer_lines_lookup.end()) if (it == all_buffer_lines_lookup.end())
all_buffer_lines_lookup[buffer_line] = { i + 1 }; all_buffer_lines_lookup[buffer_line] = {i + 1};
else else
it->second.push_back(i + 1); it->second.push_back(i + 1);
} }
@ -86,14 +84,15 @@ optional<int> WorkingFile::GetBufferLineFromIndexLine(int index_line) const {
// Note: |index_line| and |buffer_line| are 1-based. // Note: |index_line| and |buffer_line| are 1-based.
// TODO: reenable this assert once we are using the real indexed file. // TODO: reenable this assert once we are using the real indexed file.
//assert(index_line >= 1 && index_line <= index_lines.size()); // assert(index_line >= 1 && index_line <= index_lines.size());
if (index_line < 1 || index_line > index_lines.size()) { if (index_line < 1 || index_line > index_lines.size()) {
std::cerr << "!! Bad index_line (got " << index_line << ", expected [1, " << index_lines.size() << "])" << std::endl; std::cerr << "!! Bad index_line (got " << index_line << ", expected [1, "
<< index_lines.size() << "])" << std::endl;
return nullopt; return nullopt;
} }
// Find the line in the cached index file. We'll try to find the most similar line // Find the line in the cached index file. We'll try to find the most similar
// in the buffer and return the index for that. // line in the buffer and return the index for that.
std::string index = index_lines[index_line - 1]; std::string index = index_lines[index_line - 1];
auto buffer_it = all_buffer_lines_lookup.find(index); auto buffer_it = all_buffer_lines_lookup.find(index);
if (buffer_it == all_buffer_lines_lookup.end()) { if (buffer_it == all_buffer_lines_lookup.end()) {
@ -122,9 +121,10 @@ optional<int> WorkingFile::GetIndexLineFromBufferLine(int buffer_line) const {
// See GetBufferLineFromIndexLine for additional comments. // See GetBufferLineFromIndexLine for additional comments.
// Note: |index_line| and |buffer_line| are 1-based. // Note: |index_line| and |buffer_line| are 1-based.
//assert(buffer_line >= 1 && buffer_line < all_buffer_lines.size()); // assert(buffer_line >= 1 && buffer_line < all_buffer_lines.size());
if (buffer_line < 1 || buffer_line > all_buffer_lines.size()) { if (buffer_line < 1 || buffer_line > all_buffer_lines.size()) {
std::cerr << "!! Bad buffer_line (got " << buffer_line << ", expected [1, " << all_buffer_lines.size() << "])" << std::endl; std::cerr << "!! Bad buffer_line (got " << buffer_line << ", expected [1, "
<< all_buffer_lines.size() << "])" << std::endl;
return nullopt; return nullopt;
} }
@ -154,7 +154,9 @@ optional<int> WorkingFile::GetIndexLineFromBufferLine(int buffer_line) const {
return closest_index_line; return closest_index_line;
} }
optional<std::string> WorkingFile::GetBufferLineContentFromIndexLine(int indexed_line, optional<int>* out_buffer_line) const { optional<std::string> WorkingFile::GetBufferLineContentFromIndexLine(
int indexed_line,
optional<int>* out_buffer_line) const {
optional<int> buffer_line = GetBufferLineFromIndexLine(indexed_line); optional<int> buffer_line = GetBufferLineFromIndexLine(indexed_line);
if (out_buffer_line) if (out_buffer_line)
*out_buffer_line = buffer_line; *out_buffer_line = buffer_line;
@ -163,14 +165,19 @@ optional<std::string> WorkingFile::GetBufferLineContentFromIndexLine(int indexed
return nullopt; return nullopt;
if (*buffer_line < 1 || *buffer_line >= all_buffer_lines.size()) { if (*buffer_line < 1 || *buffer_line >= all_buffer_lines.size()) {
std::cerr << "GetBufferLineContentFromIndexLine buffer line lookup not in all_buffer_lines" << std::endl; std::cerr << "GetBufferLineContentFromIndexLine buffer line lookup not in "
"all_buffer_lines"
<< std::endl;
return nullopt; return nullopt;
} }
return all_buffer_lines[*buffer_line - 1]; return all_buffer_lines[*buffer_line - 1];
} }
std::string WorkingFile::FindClosestCallNameInBuffer(lsPosition position, int* active_parameter, lsPosition* completion_position) const { std::string WorkingFile::FindClosestCallNameInBuffer(
lsPosition position,
int* active_parameter,
lsPosition* completion_position) const {
*active_parameter = 0; *active_parameter = 0;
int offset = GetOffsetForPosition(position, buffer_content); int offset = GetOffsetForPosition(position, buffer_content);
@ -184,8 +191,10 @@ std::string WorkingFile::FindClosestCallNameInBuffer(lsPosition position, int* a
int balance = 0; int balance = 0;
while (offset > 0) { while (offset > 0) {
char c = buffer_content[offset]; char c = buffer_content[offset];
if (c == ')') ++balance; if (c == ')')
else if (c == '(') --balance; ++balance;
else if (c == '(')
--balance;
if (balance == 0 && c == ',') if (balance == 0 && c == ',')
*active_parameter += 1; *active_parameter += 1;
@ -214,7 +223,10 @@ std::string WorkingFile::FindClosestCallNameInBuffer(lsPosition position, int* a
return buffer_content.substr(offset, start_offset - offset + 1); return buffer_content.substr(offset, start_offset - offset + 1);
} }
lsPosition WorkingFile::FindStableCompletionSource(lsPosition position, bool* is_global_completion, std::string* existing_completion) const { lsPosition WorkingFile::FindStableCompletionSource(
lsPosition position,
bool* is_global_completion,
std::string* existing_completion) const {
*is_global_completion = true; *is_global_completion = true;
int start_offset = GetOffsetForPosition(position, buffer_content); int start_offset = GetOffsetForPosition(position, buffer_content);
@ -257,7 +269,8 @@ WorkingFile* WorkingFiles::GetFileByFilename(const std::string& filename) {
return GetFileByFilenameNoLock(filename); return GetFileByFilenameNoLock(filename);
} }
WorkingFile* WorkingFiles::GetFileByFilenameNoLock(const std::string& filename) { WorkingFile* WorkingFiles::GetFileByFilenameNoLock(
const std::string& filename) {
for (auto& file : files) { for (auto& file : files) {
if (file->filename == filename) if (file->filename == filename)
return file.get(); return file.get();
@ -273,7 +286,6 @@ void WorkingFiles::DoAction(const std::function<void()>& action) {
void WorkingFiles::DoActionOnFile( void WorkingFiles::DoActionOnFile(
const std::string& filename, const std::string& filename,
const std::function<void(WorkingFile* file)>& action) { const std::function<void(WorkingFile* file)>& action) {
std::lock_guard<std::mutex> lock(files_mutex); std::lock_guard<std::mutex> lock(files_mutex);
WorkingFile* file = GetFileByFilenameNoLock(filename); WorkingFile* file = GetFileByFilenameNoLock(filename);
action(file); action(file);
@ -303,28 +315,33 @@ void WorkingFiles::OnChange(const Ipc_TextDocumentDidChange::Params& change) {
std::string filename = change.textDocument.uri.GetPath(); std::string filename = change.textDocument.uri.GetPath();
WorkingFile* file = GetFileByFilenameNoLock(filename); WorkingFile* file = GetFileByFilenameNoLock(filename);
if (!file) { if (!file) {
std::cerr << "Could not change " << filename << " because it was not open" << std::endl; std::cerr << "Could not change " << filename << " because it was not open"
<< std::endl;
return; return;
} }
file->version = change.textDocument.version; file->version = change.textDocument.version;
// std::cerr << "!!!!!!!!!!!!!!!!!!!!!!!!!" << std::endl; // std::cerr << "!!!!!!!!!!!!!!!!!!!!!!!!!" << std::endl;
//std::cerr << "VERSION " << change.textDocument.version << std::endl; // std::cerr << "VERSION " << change.textDocument.version << std::endl;
for (const Ipc_TextDocumentDidChange::lsTextDocumentContentChangeEvent& diff : change.contentChanges) { for (const Ipc_TextDocumentDidChange::lsTextDocumentContentChangeEvent& diff :
change.contentChanges) {
// std::cerr << "|" << file->buffer_content << "|" << std::endl; // std::cerr << "|" << file->buffer_content << "|" << std::endl;
// If range or rangeLength are emitted we replace everything, per the spec. // If range or rangeLength are emitted we replace everything, per the spec.
if (diff.rangeLength == -1) { if (diff.rangeLength == -1) {
file->buffer_content = diff.text; file->buffer_content = diff.text;
file->OnBufferContentUpdated(); file->OnBufferContentUpdated();
// std::cerr << "-> Replacing entire content"; // std::cerr << "-> Replacing entire content";
} } else {
else { int start_offset =
int start_offset = GetOffsetForPosition(diff.range.start, file->buffer_content); GetOffsetForPosition(diff.range.start, file->buffer_content);
// std::cerr << "-> Applying diff start=" << diff.range.start.ToString() << ", end=" << diff.range.end.ToString() << ", start_offset=" << start_offset << std::endl; // std::cerr << "-> Applying diff start=" << diff.range.start.ToString()
file->buffer_content.replace(file->buffer_content.begin() + start_offset, // << ", end=" << diff.range.end.ToString() << ", start_offset=" <<
file->buffer_content.begin() + start_offset + diff.rangeLength, // start_offset << std::endl;
diff.text); file->buffer_content.replace(
file->buffer_content.begin() + start_offset,
file->buffer_content.begin() + start_offset + diff.rangeLength,
diff.text);
file->OnBufferContentUpdated(); file->OnBufferContentUpdated();
} }
@ -332,7 +349,8 @@ void WorkingFiles::OnChange(const Ipc_TextDocumentDidChange::Params& change) {
} }
// std::cerr << "!!!!!!!!!!!!!!!!!!!!!!!!!" << std::endl; // std::cerr << "!!!!!!!!!!!!!!!!!!!!!!!!!" << std::endl;
//std::cerr << std::endl << std::endl << "--------" << file->content << "--------" << std::endl << std::endl; // std::cerr << std::endl << std::endl << "--------" << file->content <<
// "--------" << std::endl << std::endl;
} }
void WorkingFiles::OnClose(const Ipc_TextDocumentDidClose::Params& close) { void WorkingFiles::OnClose(const Ipc_TextDocumentDidClose::Params& close) {
@ -347,7 +365,8 @@ void WorkingFiles::OnClose(const Ipc_TextDocumentDidClose::Params& close) {
} }
} }
std::cerr << "Could not close " << filename << " because it was not open" << std::endl; std::cerr << "Could not close " << filename << " because it was not open"
<< std::endl;
} }
std::vector<CXUnsavedFile> WorkingFiles::AsUnsavedFiles() { std::vector<CXUnsavedFile> WorkingFiles::AsUnsavedFiles() {
@ -362,82 +381,108 @@ std::vector<CXUnsavedFile> WorkingFiles::AsUnsavedFiles() {
TEST_SUITE("WorkingFile"); TEST_SUITE("WorkingFile");
lsPosition CharPos(const WorkingFile& file, char character, int character_offset = 0) { lsPosition CharPos(const WorkingFile& file,
char character,
int character_offset = 0) {
return CharPos(file.buffer_content, character, character_offset); return CharPos(file.buffer_content, character, character_offset);
} }
TEST_CASE("simple call") { TEST_CASE("simple call") {
WorkingFile f("foo.cc", "abcd(1, 2"); WorkingFile f("foo.cc", "abcd(1, 2");
int active_param = 0; int active_param = 0;
REQUIRE(f.FindClosestCallNameInBuffer(CharPos(f, '('), &active_param) == "abcd"); REQUIRE(f.FindClosestCallNameInBuffer(CharPos(f, '('), &active_param) ==
"abcd");
REQUIRE(active_param == 0); REQUIRE(active_param == 0);
REQUIRE(f.FindClosestCallNameInBuffer(CharPos(f, '1'), &active_param) == "abcd"); REQUIRE(f.FindClosestCallNameInBuffer(CharPos(f, '1'), &active_param) ==
"abcd");
REQUIRE(active_param == 0); REQUIRE(active_param == 0);
REQUIRE(f.FindClosestCallNameInBuffer(CharPos(f, ','), &active_param) == "abcd"); REQUIRE(f.FindClosestCallNameInBuffer(CharPos(f, ','), &active_param) ==
"abcd");
REQUIRE(active_param == 1); REQUIRE(active_param == 1);
REQUIRE(f.FindClosestCallNameInBuffer(CharPos(f, ' '), &active_param) == "abcd"); REQUIRE(f.FindClosestCallNameInBuffer(CharPos(f, ' '), &active_param) ==
"abcd");
REQUIRE(active_param == 1); REQUIRE(active_param == 1);
REQUIRE(f.FindClosestCallNameInBuffer(CharPos(f, '2'), &active_param) == "abcd"); REQUIRE(f.FindClosestCallNameInBuffer(CharPos(f, '2'), &active_param) ==
"abcd");
REQUIRE(active_param == 1); REQUIRE(active_param == 1);
} }
TEST_CASE("nested call") { TEST_CASE("nested call") {
WorkingFile f("foo.cc", "abcd(efg(), 2"); WorkingFile f("foo.cc", "abcd(efg(), 2");
int active_param = 0; int active_param = 0;
REQUIRE(f.FindClosestCallNameInBuffer(CharPos(f, '('), &active_param) == "abcd"); REQUIRE(f.FindClosestCallNameInBuffer(CharPos(f, '('), &active_param) ==
"abcd");
REQUIRE(active_param == 0); REQUIRE(active_param == 0);
REQUIRE(f.FindClosestCallNameInBuffer(CharPos(f, 'e'), &active_param) == "abcd"); REQUIRE(f.FindClosestCallNameInBuffer(CharPos(f, 'e'), &active_param) ==
"abcd");
REQUIRE(active_param == 0); REQUIRE(active_param == 0);
REQUIRE(f.FindClosestCallNameInBuffer(CharPos(f, 'f'), &active_param) == "abcd"); REQUIRE(f.FindClosestCallNameInBuffer(CharPos(f, 'f'), &active_param) ==
"abcd");
REQUIRE(active_param == 0); REQUIRE(active_param == 0);
REQUIRE(f.FindClosestCallNameInBuffer(CharPos(f, 'g'), &active_param) == "abcd"); REQUIRE(f.FindClosestCallNameInBuffer(CharPos(f, 'g'), &active_param) ==
"abcd");
REQUIRE(active_param == 0); REQUIRE(active_param == 0);
REQUIRE(f.FindClosestCallNameInBuffer(CharPos(f, 'g', 1), &active_param) == "efg"); REQUIRE(f.FindClosestCallNameInBuffer(CharPos(f, 'g', 1), &active_param) ==
"efg");
REQUIRE(active_param == 0); REQUIRE(active_param == 0);
REQUIRE(f.FindClosestCallNameInBuffer(CharPos(f, 'g', 2), &active_param) == "efg"); REQUIRE(f.FindClosestCallNameInBuffer(CharPos(f, 'g', 2), &active_param) ==
"efg");
REQUIRE(active_param == 0); REQUIRE(active_param == 0);
REQUIRE(f.FindClosestCallNameInBuffer(CharPos(f, ','), &active_param) == "abcd"); REQUIRE(f.FindClosestCallNameInBuffer(CharPos(f, ','), &active_param) ==
"abcd");
REQUIRE(active_param == 1); REQUIRE(active_param == 1);
REQUIRE(f.FindClosestCallNameInBuffer(CharPos(f, ' '), &active_param) == "abcd"); REQUIRE(f.FindClosestCallNameInBuffer(CharPos(f, ' '), &active_param) ==
"abcd");
REQUIRE(active_param == 1); REQUIRE(active_param == 1);
} }
TEST_CASE("auto-insert )") { TEST_CASE("auto-insert )") {
WorkingFile f("foo.cc", "abc()"); WorkingFile f("foo.cc", "abc()");
int active_param = 0; int active_param = 0;
REQUIRE(f.FindClosestCallNameInBuffer(CharPos(f, ')'), &active_param) == "abc"); REQUIRE(f.FindClosestCallNameInBuffer(CharPos(f, ')'), &active_param) ==
"abc");
REQUIRE(active_param == 0); REQUIRE(active_param == 0);
} }
TEST_CASE("existing completion") { TEST_CASE("existing completion") {
// TODO: remove trailing space in zz.asdf. Lexing doesn't work correctly if done at the end of input. // TODO: remove trailing space in zz.asdf. Lexing doesn't work correctly if
// done at the end of input.
WorkingFile f("foo.cc", "zzz.asdf "); WorkingFile f("foo.cc", "zzz.asdf ");
bool is_global_completion; bool is_global_completion;
std::string existing_completion; std::string existing_completion;
f.FindStableCompletionSource(CharPos(f, '.'), &is_global_completion, &existing_completion); f.FindStableCompletionSource(CharPos(f, '.'), &is_global_completion,
&existing_completion);
REQUIRE(existing_completion == "zzz"); REQUIRE(existing_completion == "zzz");
f.FindStableCompletionSource(CharPos(f, 'a', 1), &is_global_completion, &existing_completion); f.FindStableCompletionSource(CharPos(f, 'a', 1), &is_global_completion,
&existing_completion);
REQUIRE(existing_completion == "a"); REQUIRE(existing_completion == "a");
f.FindStableCompletionSource(CharPos(f, 's', 1), &is_global_completion, &existing_completion); f.FindStableCompletionSource(CharPos(f, 's', 1), &is_global_completion,
&existing_completion);
REQUIRE(existing_completion == "as"); REQUIRE(existing_completion == "as");
f.FindStableCompletionSource(CharPos(f, 'd', 1), &is_global_completion, &existing_completion); f.FindStableCompletionSource(CharPos(f, 'd', 1), &is_global_completion,
&existing_completion);
REQUIRE(existing_completion == "asd"); REQUIRE(existing_completion == "asd");
f.FindStableCompletionSource(CharPos(f, 'f', 1), &is_global_completion, &existing_completion); f.FindStableCompletionSource(CharPos(f, 'f', 1), &is_global_completion,
&existing_completion);
REQUIRE(existing_completion == "asdf"); REQUIRE(existing_completion == "asdf");
} }
TEST_CASE("existing completion underscore") { TEST_CASE("existing completion underscore") {
// TODO: remove trailing space in ABC_DEF. Lexing doesn't work correctly if done at the end of input. // TODO: remove trailing space in ABC_DEF. Lexing doesn't work correctly if
// done at the end of input.
WorkingFile f("foo.cc", "ABC_DEF "); WorkingFile f("foo.cc", "ABC_DEF ");
bool is_global_completion; bool is_global_completion;
std::string existing_completion; std::string existing_completion;
f.FindStableCompletionSource(CharPos(f, 'C'), &is_global_completion, &existing_completion); f.FindStableCompletionSource(CharPos(f, 'C'), &is_global_completion,
&existing_completion);
REQUIRE(existing_completion == "AB"); REQUIRE(existing_completion == "AB");
f.FindStableCompletionSource(CharPos(f, '_'), &is_global_completion, &existing_completion); f.FindStableCompletionSource(CharPos(f, '_'), &is_global_completion,
&existing_completion);
REQUIRE(existing_completion == "ABC"); REQUIRE(existing_completion == "ABC");
f.FindStableCompletionSource(CharPos(f, 'D'), &is_global_completion, &existing_completion); f.FindStableCompletionSource(CharPos(f, 'D'), &is_global_completion,
&existing_completion);
REQUIRE(existing_completion == "ABC_"); REQUIRE(existing_completion == "ABC_");
} }

View File

@ -9,8 +9,8 @@
#include <mutex> #include <mutex>
#include <string> #include <string>
using std::experimental::optional;
using std::experimental::nullopt; using std::experimental::nullopt;
using std::experimental::optional;
struct WorkingFile { struct WorkingFile {
int version = 0; int version = 0;
@ -28,7 +28,8 @@ struct WorkingFile {
// Note: The items in the value entry are 1-based liness. // Note: The items in the value entry are 1-based liness.
std::unordered_map<std::string, std::vector<int>> all_buffer_lines_lookup; std::unordered_map<std::string, std::vector<int>> all_buffer_lines_lookup;
// A set of diagnostics that have been reported for this file. // A set of diagnostics that have been reported for this file.
// NOTE: _ is appended because it must be accessed under the WorkingFiles lock! // NOTE: _ is appended because it must be accessed under the WorkingFiles
// lock!
std::vector<lsDiagnostic> diagnostics_; std::vector<lsDiagnostic> diagnostics_;
WorkingFile(const std::string& filename, const std::string& buffer_content); WorkingFile(const std::string& filename, const std::string& buffer_content);
@ -45,16 +46,22 @@ struct WorkingFile {
// accepts and returns 1-based lines. // accepts and returns 1-based lines.
optional<int> GetIndexLineFromBufferLine(int buffer_line) const; optional<int> GetIndexLineFromBufferLine(int buffer_line) const;
optional<std::string> GetBufferLineContentFromIndexLine(int indexed_line, optional<int>* out_buffer_line) const; optional<std::string> GetBufferLineContentFromIndexLine(
int indexed_line,
optional<int>* out_buffer_line) const;
// TODO: Move FindClosestCallNameInBuffer and FindStableCompletionSource into lex_utils.h/cc // TODO: Move FindClosestCallNameInBuffer and FindStableCompletionSource into
// lex_utils.h/cc
// Finds the closest 'callable' name prior to position. This is used for // Finds the closest 'callable' name prior to position. This is used for
// signature help to filter code completion results. // signature help to filter code completion results.
// //
// |completion_position| will be point to a good code completion location to // |completion_position| will be point to a good code completion location to
// for fetching signatures. // for fetching signatures.
std::string FindClosestCallNameInBuffer(lsPosition position, int* active_parameter, lsPosition* completion_position = nullptr) const; std::string FindClosestCallNameInBuffer(
lsPosition position,
int* active_parameter,
lsPosition* completion_position = nullptr) const;
// Returns a relatively stable completion position (it jumps back until there // Returns a relatively stable completion position (it jumps back until there
// is a non-alphanumeric character). // is a non-alphanumeric character).
@ -63,7 +70,9 @@ struct WorkingFile {
// global completion. // global completion.
// The out param |existing_completion| is set to any existing completion // The out param |existing_completion| is set to any existing completion
// content the user has entered. // content the user has entered.
lsPosition FindStableCompletionSource(lsPosition position, bool* is_global_completion, std::string* existing_completion) const; lsPosition FindStableCompletionSource(lsPosition position,
bool* is_global_completion,
std::string* existing_completion) const;
CXUnsavedFile AsUnsavedFile() const; CXUnsavedFile AsUnsavedFile() const;
}; };
@ -79,8 +88,10 @@ struct WorkingFiles {
// Run |action| under the lock. // Run |action| under the lock.
void DoAction(const std::function<void()>& action); void DoAction(const std::function<void()>& action);
// Run |action| on the file identified by |filename|. This executes under the lock. // Run |action| on the file identified by |filename|. This executes under the
void DoActionOnFile(const std::string& filename, const std::function<void(WorkingFile* file)>& action); // lock.
void DoActionOnFile(const std::string& filename,
const std::function<void(WorkingFile* file)>& action);
WorkingFile* OnOpen(const Ipc_TextDocumentDidOpen::Params& open); WorkingFile* OnOpen(const Ipc_TextDocumentDidOpen::Params& open);
void OnChange(const Ipc_TextDocumentDidChange::Params& change); void OnChange(const Ipc_TextDocumentDidChange::Params& change);
@ -91,5 +102,5 @@ struct WorkingFiles {
// Use unique_ptrs so we can handout WorkingFile ptrs and not have them // Use unique_ptrs so we can handout WorkingFile ptrs and not have them
// invalidated if we resize files. // invalidated if we resize files.
std::vector<std::unique_ptr<WorkingFile>> files; std::vector<std::unique_ptr<WorkingFile>> files;
std::mutex files_mutex; // Protects |files|. std::mutex files_mutex; // Protects |files|.
}; };