2017-03-25 20:15:00 +00:00
|
|
|
// TODO: cleanup includes
|
2017-04-09 02:24:32 +00:00
|
|
|
#include "cache.h"
|
2017-12-05 07:57:41 +00:00
|
|
|
#include "cache_loader.h"
|
2017-05-27 04:21:00 +00:00
|
|
|
#include "clang_complete.h"
|
2017-04-08 22:54:36 +00:00
|
|
|
#include "file_consumer.h"
|
2017-05-27 04:21:00 +00:00
|
|
|
#include "include_complete.h"
|
2017-02-25 23:59:09 +00:00
|
|
|
#include "indexer.h"
|
2017-09-22 01:14:57 +00:00
|
|
|
#include "ipc_manager.h"
|
2017-03-05 02:16:23 +00:00
|
|
|
#include "language_server_api.h"
|
2017-06-15 05:32:23 +00:00
|
|
|
#include "lex_utils.h"
|
2017-12-04 02:23:14 +00:00
|
|
|
#include "lru_cache.h"
|
2017-09-22 01:14:57 +00:00
|
|
|
#include "match.h"
|
2017-12-05 07:57:41 +00:00
|
|
|
#include "message_handler.h"
|
2017-04-09 02:24:32 +00:00
|
|
|
#include "options.h"
|
2017-03-25 20:27:28 +00:00
|
|
|
#include "platform.h"
|
2017-09-22 01:14:57 +00:00
|
|
|
#include "project.h"
|
|
|
|
#include "query.h"
|
|
|
|
#include "query_utils.h"
|
2017-07-30 04:24:02 +00:00
|
|
|
#include "serializer.h"
|
2017-05-21 07:37:53 +00:00
|
|
|
#include "standard_includes.h"
|
2017-03-10 07:06:01 +00:00
|
|
|
#include "test.h"
|
2017-03-25 20:15:00 +00:00
|
|
|
#include "threaded_queue.h"
|
2017-09-22 01:14:57 +00:00
|
|
|
#include "timer.h"
|
2017-12-05 07:57:41 +00:00
|
|
|
#include "timestamp_manager.h"
|
2017-09-13 03:35:27 +00:00
|
|
|
#include "work_thread.h"
|
2017-03-26 21:40:34 +00:00
|
|
|
#include "working_files.h"
|
2017-03-05 02:16:23 +00:00
|
|
|
|
2017-03-25 20:27:28 +00:00
|
|
|
#include <doctest/doctest.h>
|
|
|
|
#include <rapidjson/istreamwrapper.h>
|
|
|
|
#include <rapidjson/ostreamwrapper.h>
|
2017-11-04 22:29:03 +00:00
|
|
|
#include <loguru.hpp>
|
2017-03-25 20:27:28 +00:00
|
|
|
|
2017-06-10 01:02:48 +00:00
|
|
|
#include <climits>
|
2017-03-31 05:30:50 +00:00
|
|
|
#include <fstream>
|
2017-04-17 01:22:59 +00:00
|
|
|
#include <functional>
|
2017-09-22 01:14:57 +00:00
|
|
|
#include <future>
|
2017-03-25 20:27:28 +00:00
|
|
|
#include <iostream>
|
2017-05-21 07:37:53 +00:00
|
|
|
#include <iterator>
|
2017-05-17 07:08:45 +00:00
|
|
|
#include <sstream>
|
2017-09-22 01:14:57 +00:00
|
|
|
#include <string>
|
2017-03-25 20:27:28 +00:00
|
|
|
#include <thread>
|
2017-09-22 01:14:57 +00:00
|
|
|
#include <unordered_map>
|
2017-03-25 20:27:28 +00:00
|
|
|
#include <vector>
|
2017-02-25 23:59:09 +00:00
|
|
|
|
2017-04-11 07:29:36 +00:00
|
|
|
// TODO: provide a feature like 'https://github.com/goldsborough/clang-expand',
|
|
|
|
// ie, a fully linear view of a function with inline function calls expanded.
|
|
|
|
// We can probably use vscode decorators to achieve it.
|
|
|
|
|
2017-09-22 01:14:57 +00:00
|
|
|
// TODO: implement ThreadPool type which monitors CPU usage / number of work
|
|
|
|
// items per second completed and scales up/down number of running threads.
|
2017-05-20 19:31:07 +00:00
|
|
|
|
2017-08-17 02:08:00 +00:00
|
|
|
namespace {
|
2017-04-09 02:24:32 +00:00
|
|
|
|
2017-04-21 04:06:15 +00:00
|
|
|
std::vector<std::string> kEmptyArgs;
|
2017-04-16 19:02:29 +00:00
|
|
|
|
2017-11-30 04:22:10 +00:00
|
|
|
// If true stdout will be printed to stderr.
|
|
|
|
bool g_log_stdin_stdout_to_stderr = false;
|
|
|
|
|
2017-09-22 01:14:57 +00:00
|
|
|
// This function returns true if e2e timing should be displayed for the given
|
|
|
|
// IpcId.
|
2017-05-20 08:07:29 +00:00
|
|
|
bool ShouldDisplayIpcTiming(IpcId id) {
|
|
|
|
switch (id) {
|
2017-09-22 01:14:57 +00:00
|
|
|
case IpcId::TextDocumentPublishDiagnostics:
|
|
|
|
case IpcId::CqueryPublishInactiveRegions:
|
2017-12-04 08:16:19 +00:00
|
|
|
case IpcId::Unknown:
|
2017-09-22 01:14:57 +00:00
|
|
|
return false;
|
|
|
|
default:
|
|
|
|
return true;
|
2017-05-20 08:07:29 +00:00
|
|
|
}
|
|
|
|
}
|
2017-04-16 19:02:29 +00:00
|
|
|
|
2017-09-22 01:14:57 +00:00
|
|
|
void EmitDiagnostics(WorkingFiles* working_files,
|
|
|
|
std::string path,
|
|
|
|
NonElidedVector<lsDiagnostic> diagnostics) {
|
2017-06-10 04:13:16 +00:00
|
|
|
// Emit diagnostics.
|
2017-12-04 08:13:35 +00:00
|
|
|
Out_TextDocumentPublishDiagnostics out;
|
|
|
|
out.params.uri = lsDocumentUri::FromPath(path);
|
|
|
|
out.params.diagnostics = diagnostics;
|
|
|
|
IpcManager::WriteStdout(IpcId::TextDocumentPublishDiagnostics, out);
|
2017-06-10 04:13:16 +00:00
|
|
|
|
|
|
|
// Cache diagnostics so we can show fixits.
|
2017-06-14 06:29:41 +00:00
|
|
|
working_files->DoActionOnFile(path, [&](WorkingFile* working_file) {
|
|
|
|
if (working_file)
|
|
|
|
working_file->diagnostics_ = diagnostics;
|
|
|
|
});
|
2017-06-10 04:13:16 +00:00
|
|
|
}
|
2017-04-15 05:14:05 +00:00
|
|
|
|
2017-12-06 04:05:01 +00:00
|
|
|
REGISTER_IPC_MESSAGE(Ipc_CancelRequest);
|
2017-03-02 09:28:07 +00:00
|
|
|
|
2017-11-26 22:20:43 +00:00
|
|
|
// Send indexing progress to client if reporting is enabled.
|
2017-12-01 17:50:39 +00:00
|
|
|
void EmitProgress(Config* config, QueueManager* queue) {
|
2017-11-26 22:20:43 +00:00
|
|
|
if (config->enableProgressReports) {
|
|
|
|
Out_Progress out;
|
|
|
|
out.params.indexRequestCount = queue->index_request.Size();
|
|
|
|
out.params.doIdMapCount = queue->do_id_map.Size();
|
|
|
|
out.params.loadPreviousIndexCount = queue->load_previous_index.Size();
|
|
|
|
out.params.onIdMappedCount = queue->on_id_mapped.Size();
|
|
|
|
out.params.onIndexedCount = queue->on_indexed.Size();
|
|
|
|
|
2017-12-04 08:16:19 +00:00
|
|
|
IpcManager::WriteStdout(IpcId::Unknown, out);
|
2017-11-26 22:20:43 +00:00
|
|
|
}
|
2017-10-25 07:12:11 +00:00
|
|
|
}
|
|
|
|
|
2017-08-17 02:08:00 +00:00
|
|
|
} // namespace
|
2017-04-23 20:02:41 +00:00
|
|
|
|
2017-09-22 01:32:55 +00:00
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// IMPORT PIPELINE /////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
2017-08-16 05:39:50 +00:00
|
|
|
|
2017-10-12 15:40:51 +00:00
|
|
|
enum class FileParseQuery { NeedsParse, DoesNotNeedParse, NoSuchFile };
|
2017-09-22 02:25:33 +00:00
|
|
|
|
2017-08-15 07:22:13 +00:00
|
|
|
std::vector<Index_DoIdMap> DoParseFile(
|
2017-08-17 03:39:00 +00:00
|
|
|
Config* config,
|
|
|
|
WorkingFiles* working_files,
|
2017-11-11 19:41:09 +00:00
|
|
|
ClangIndex* index,
|
2017-08-17 03:39:00 +00:00
|
|
|
FileConsumer::SharedState* file_consumer_shared,
|
|
|
|
TimestampManager* timestamp_manager,
|
2017-09-20 05:08:17 +00:00
|
|
|
ImportManager* import_manager,
|
2017-08-17 03:39:00 +00:00
|
|
|
CacheLoader* cache_loader,
|
|
|
|
bool is_interactive,
|
|
|
|
const std::string& path,
|
2017-09-13 03:35:27 +00:00
|
|
|
const std::vector<std::string>& args,
|
|
|
|
const optional<FileContents>& contents) {
|
2017-08-15 07:22:13 +00:00
|
|
|
std::vector<Index_DoIdMap> result;
|
2017-08-15 05:53:44 +00:00
|
|
|
|
2017-09-24 00:36:28 +00:00
|
|
|
// Always run this block, even if we are interactive, so we can check
|
|
|
|
// dependencies and reset files in |file_consumer_shared|.
|
2017-08-15 05:53:44 +00:00
|
|
|
IndexFile* previous_index = cache_loader->TryLoad(path);
|
2017-09-24 00:36:28 +00:00
|
|
|
if (previous_index) {
|
2017-09-20 05:08:17 +00:00
|
|
|
// If none of the dependencies have changed and the index is not
|
|
|
|
// interactive (ie, requested by a file save), skip parsing and just load
|
|
|
|
// from cache.
|
2017-09-14 06:39:32 +00:00
|
|
|
|
|
|
|
// Checks if |path| needs to be reparsed. This will modify cached state
|
|
|
|
// such that calling this function twice with the same path may return true
|
|
|
|
// the first time but will return false the second.
|
2017-09-20 05:08:17 +00:00
|
|
|
auto file_needs_parse = [&](const std::string& path, bool is_dependency) {
|
|
|
|
// If the file is a dependency but another file as already imported it,
|
|
|
|
// don't bother.
|
2017-09-24 00:36:28 +00:00
|
|
|
if (!is_interactive && is_dependency &&
|
|
|
|
!import_manager->TryMarkDependencyImported(path)) {
|
2017-09-20 05:08:17 +00:00
|
|
|
return FileParseQuery::DoesNotNeedParse;
|
2017-09-24 00:36:28 +00:00
|
|
|
}
|
2017-09-20 05:08:17 +00:00
|
|
|
|
2017-08-16 05:39:50 +00:00
|
|
|
optional<int64_t> modification_timestamp = GetLastModificationTime(path);
|
2017-10-12 15:40:51 +00:00
|
|
|
|
|
|
|
// Cannot find file.
|
2017-08-16 05:39:50 +00:00
|
|
|
if (!modification_timestamp)
|
2017-10-12 15:40:51 +00:00
|
|
|
return FileParseQuery::NoSuchFile;
|
2017-08-16 05:39:50 +00:00
|
|
|
|
2017-09-22 01:14:57 +00:00
|
|
|
optional<int64_t> last_cached_modification =
|
|
|
|
timestamp_manager->GetLastCachedModificationTime(cache_loader, path);
|
2017-08-16 03:54:59 +00:00
|
|
|
|
2017-10-12 15:40:51 +00:00
|
|
|
// File has been changed.
|
2017-09-22 01:14:57 +00:00
|
|
|
if (!last_cached_modification ||
|
|
|
|
modification_timestamp != *last_cached_modification) {
|
2017-08-15 05:53:44 +00:00
|
|
|
file_consumer_shared->Reset(path);
|
2017-09-22 01:14:57 +00:00
|
|
|
timestamp_manager->UpdateCachedModificationTime(
|
|
|
|
path, *modification_timestamp);
|
2017-08-16 05:45:42 +00:00
|
|
|
return FileParseQuery::NeedsParse;
|
2017-08-15 05:53:44 +00:00
|
|
|
}
|
2017-10-12 15:40:51 +00:00
|
|
|
|
|
|
|
// File has not changed, do not parse it.
|
2017-08-16 05:45:42 +00:00
|
|
|
return FileParseQuery::DoesNotNeedParse;
|
2017-08-15 05:53:44 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
// Check timestamps and update |file_consumer_shared|.
|
2017-09-20 05:08:17 +00:00
|
|
|
FileParseQuery path_state = file_needs_parse(path, false /*is_dependency*/);
|
2017-10-12 15:40:51 +00:00
|
|
|
|
|
|
|
// Target file does not exist on disk, do not emit any indexes.
|
|
|
|
// TODO: Dependencies should be reassigned to other files. We can do this by
|
|
|
|
// updating the "primary_file" if it doesn't exist. Might not actually be a
|
|
|
|
// problem in practice.
|
|
|
|
if (path_state == FileParseQuery::NoSuchFile)
|
2017-08-16 05:39:50 +00:00
|
|
|
return result;
|
2017-10-12 15:40:51 +00:00
|
|
|
|
2017-09-24 00:36:28 +00:00
|
|
|
bool needs_reparse =
|
|
|
|
is_interactive || path_state == FileParseQuery::NeedsParse;
|
2017-08-16 05:39:50 +00:00
|
|
|
|
2017-08-15 05:53:44 +00:00
|
|
|
for (const std::string& dependency : previous_index->dependencies) {
|
2017-08-16 03:29:49 +00:00
|
|
|
assert(!dependency.empty());
|
|
|
|
|
2017-10-12 15:40:51 +00:00
|
|
|
// note: Use != as there are multiple failure results for FileParseQuery.
|
|
|
|
if (file_needs_parse(dependency, true /*is_dependency*/) !=
|
|
|
|
FileParseQuery::DoesNotNeedParse) {
|
2017-09-22 01:14:57 +00:00
|
|
|
LOG_S(INFO) << "Timestamp has changed for " << dependency << " (via "
|
|
|
|
<< previous_index->path << ")";
|
2017-08-15 05:53:44 +00:00
|
|
|
needs_reparse = true;
|
|
|
|
// SUBTLE: Do not break here, as |file_consumer_shared| is updated
|
|
|
|
// inside of |file_needs_parse|.
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// No timestamps changed - load directly from cache.
|
|
|
|
if (!needs_reparse) {
|
|
|
|
LOG_S(INFO) << "Skipping parse; no timestamp change for " << path;
|
|
|
|
|
|
|
|
// TODO/FIXME: real perf
|
|
|
|
PerformanceImportFile perf;
|
2017-10-12 15:40:51 +00:00
|
|
|
result.push_back(Index_DoIdMap(cache_loader->TakeOrLoad(path), perf,
|
2017-09-22 01:14:57 +00:00
|
|
|
is_interactive, false /*write_to_disk*/));
|
2017-08-15 05:53:44 +00:00
|
|
|
for (const std::string& dependency : previous_index->dependencies) {
|
2017-10-12 15:40:51 +00:00
|
|
|
// Only load a dependency if it is not already loaded.
|
|
|
|
//
|
|
|
|
// This is important for perf in large projects where there are lots of
|
|
|
|
// dependencies shared between many files.
|
2017-08-15 07:22:13 +00:00
|
|
|
if (!file_consumer_shared->Mark(dependency))
|
|
|
|
continue;
|
|
|
|
|
2017-09-22 01:14:57 +00:00
|
|
|
LOG_S(INFO) << "Emitting index result for " << dependency << " (via "
|
|
|
|
<< previous_index->path << ")";
|
2017-10-12 15:40:51 +00:00
|
|
|
|
|
|
|
std::unique_ptr<IndexFile> dependency_index =
|
|
|
|
cache_loader->TryTakeOrLoad(dependency);
|
|
|
|
|
|
|
|
// |dependency_index| may be null if there is no cache for it but
|
|
|
|
// another file has already started importing it.
|
|
|
|
if (!dependency_index)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
result.push_back(Index_DoIdMap(std::move(dependency_index), perf,
|
|
|
|
is_interactive,
|
2017-09-22 01:14:57 +00:00
|
|
|
false /*write_to_disk*/));
|
2017-08-15 05:53:44 +00:00
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
LOG_S(INFO) << "Parsing " << path;
|
|
|
|
|
|
|
|
// Load file contents for all dependencies into memory. If the dependencies
|
|
|
|
// for the file changed we may not end up using all of the files we
|
|
|
|
// preloaded. If a new dependency was added the indexer will grab the file
|
|
|
|
// contents as soon as possible.
|
|
|
|
//
|
|
|
|
// We do this to minimize the race between indexing a file and capturing the
|
|
|
|
// file contents.
|
|
|
|
//
|
|
|
|
// TODO: We might be able to optimize perf by only copying for files in
|
|
|
|
// working_files. We can pass that same set of files to the indexer as
|
|
|
|
// well. We then default to a fast file-copy if not in working set.
|
2017-08-16 05:39:50 +00:00
|
|
|
bool loaded_primary = false;
|
2017-08-15 05:53:44 +00:00
|
|
|
std::vector<FileContents> file_contents;
|
2017-09-13 03:35:27 +00:00
|
|
|
if (contents) {
|
|
|
|
loaded_primary = loaded_primary || contents->path == path;
|
|
|
|
file_contents.push_back(*contents);
|
|
|
|
}
|
2017-08-15 05:53:44 +00:00
|
|
|
for (const auto& it : cache_loader->caches) {
|
|
|
|
const std::unique_ptr<IndexFile>& index = it.second;
|
|
|
|
assert(index);
|
|
|
|
optional<std::string> index_content = ReadContent(index->path);
|
|
|
|
if (!index_content) {
|
|
|
|
LOG_S(ERROR) << "Failed to preload index content for " << index->path;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
file_contents.push_back(FileContents(index->path, *index_content));
|
2017-08-16 05:39:50 +00:00
|
|
|
|
|
|
|
loaded_primary = loaded_primary || index->path == path;
|
|
|
|
}
|
|
|
|
if (!loaded_primary) {
|
|
|
|
optional<std::string> content = ReadContent(path);
|
|
|
|
if (!content) {
|
|
|
|
LOG_S(ERROR) << "Skipping index (file cannot be found): " << path;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
file_contents.push_back(FileContents(path, *content));
|
2017-08-15 05:53:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
PerformanceImportFile perf;
|
|
|
|
std::vector<std::unique_ptr<IndexFile>> indexes = Parse(
|
2017-09-22 01:14:57 +00:00
|
|
|
config, file_consumer_shared, path, args, file_contents, &perf, index);
|
2017-08-15 05:53:44 +00:00
|
|
|
for (std::unique_ptr<IndexFile>& new_index : indexes) {
|
|
|
|
Timer time;
|
|
|
|
|
2017-10-12 15:40:51 +00:00
|
|
|
// Only emit diagnostics for non-interactive sessions, which makes it easier
|
|
|
|
// to identify indexing problems. For interactive sessions, diagnostics are
|
|
|
|
// handled by code completion.
|
|
|
|
if (!is_interactive)
|
|
|
|
EmitDiagnostics(working_files, new_index->path, new_index->diagnostics_);
|
2017-10-28 22:09:14 +00:00
|
|
|
|
2017-09-22 02:25:33 +00:00
|
|
|
// When main thread does IdMap request it will request the previous index if
|
|
|
|
// needed.
|
2017-08-15 05:53:44 +00:00
|
|
|
LOG_S(INFO) << "Emitting index result for " << new_index->path;
|
2017-09-22 01:14:57 +00:00
|
|
|
result.push_back(Index_DoIdMap(std::move(new_index), perf, is_interactive,
|
|
|
|
true /*write_to_disk*/));
|
2017-08-15 05:53:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2017-09-27 06:03:43 +00:00
|
|
|
// Index a file using an already-parsed translation unit from code completion.
|
|
|
|
// Since most of the time for indexing a file comes from parsing, we can do
|
|
|
|
// real-time indexing.
|
|
|
|
// TODO: add option to disable this.
|
|
|
|
void IndexWithTuFromCodeCompletion(
|
|
|
|
QueueManager* queue,
|
|
|
|
FileConsumer::SharedState* file_consumer_shared,
|
2017-11-11 19:41:09 +00:00
|
|
|
ClangTranslationUnit* tu,
|
2017-09-27 06:03:43 +00:00
|
|
|
const std::vector<CXUnsavedFile>& file_contents,
|
|
|
|
const std::string& path,
|
|
|
|
const std::vector<std::string>& args) {
|
|
|
|
file_consumer_shared->Reset(path);
|
|
|
|
|
|
|
|
PerformanceImportFile perf;
|
2017-11-11 19:41:09 +00:00
|
|
|
ClangIndex index;
|
2017-09-27 06:03:43 +00:00
|
|
|
std::vector<std::unique_ptr<IndexFile>> indexes = ParseWithTu(
|
|
|
|
file_consumer_shared, &perf, tu, &index, path, args, file_contents);
|
|
|
|
|
|
|
|
std::vector<Index_DoIdMap> result;
|
|
|
|
for (std::unique_ptr<IndexFile>& new_index : indexes) {
|
|
|
|
Timer time;
|
|
|
|
|
|
|
|
// When main thread does IdMap request it will request the previous index if
|
|
|
|
// needed.
|
|
|
|
LOG_S(INFO) << "Emitting index result for " << new_index->path;
|
|
|
|
result.push_back(Index_DoIdMap(std::move(new_index), perf,
|
|
|
|
true /*is_interactive*/,
|
|
|
|
true /*write_to_disk*/));
|
|
|
|
}
|
|
|
|
|
|
|
|
LOG_IF_S(WARNING, result.size() > 1)
|
|
|
|
<< "Code completion index update generated more than one index";
|
|
|
|
|
|
|
|
queue->do_id_map.EnqueueAll(std::move(result));
|
|
|
|
}
|
|
|
|
|
2017-08-15 07:22:13 +00:00
|
|
|
std::vector<Index_DoIdMap> ParseFile(
|
2017-08-15 05:53:44 +00:00
|
|
|
Config* config,
|
2017-08-17 03:39:00 +00:00
|
|
|
WorkingFiles* working_files,
|
2017-11-11 19:41:09 +00:00
|
|
|
ClangIndex* index,
|
2017-08-15 05:53:44 +00:00
|
|
|
FileConsumer::SharedState* file_consumer_shared,
|
2017-08-16 03:54:59 +00:00
|
|
|
TimestampManager* timestamp_manager,
|
2017-09-20 05:08:17 +00:00
|
|
|
ImportManager* import_manager,
|
2017-08-17 03:39:00 +00:00
|
|
|
bool is_interactive,
|
2017-09-13 03:35:27 +00:00
|
|
|
const Project::Entry& entry,
|
|
|
|
const optional<std::string>& contents) {
|
|
|
|
optional<FileContents> file_contents;
|
|
|
|
if (contents)
|
|
|
|
file_contents = FileContents(entry.filename, *contents);
|
2017-08-15 05:53:44 +00:00
|
|
|
|
|
|
|
CacheLoader cache_loader(config);
|
|
|
|
|
|
|
|
// Try to determine the original import file by loading the file from cache.
|
|
|
|
// This lets the user request an index on a header file, which clang will
|
|
|
|
// complain about if indexed by itself.
|
|
|
|
IndexFile* entry_cache = cache_loader.TryLoad(entry.filename);
|
|
|
|
std::string tu_path = entry_cache ? entry_cache->import_file : entry.filename;
|
2017-09-22 01:14:57 +00:00
|
|
|
return DoParseFile(config, working_files, index, file_consumer_shared,
|
|
|
|
timestamp_manager, import_manager, &cache_loader,
|
|
|
|
is_interactive, tu_path, entry.args, file_contents);
|
2017-08-15 05:53:44 +00:00
|
|
|
}
|
|
|
|
|
2017-09-22 01:14:57 +00:00
|
|
|
bool IndexMain_DoParse(Config* config,
|
|
|
|
WorkingFiles* working_files,
|
|
|
|
QueueManager* queue,
|
|
|
|
FileConsumer::SharedState* file_consumer_shared,
|
|
|
|
TimestampManager* timestamp_manager,
|
|
|
|
ImportManager* import_manager,
|
2017-11-11 19:41:09 +00:00
|
|
|
ClangIndex* index) {
|
2017-09-14 04:50:36 +00:00
|
|
|
optional<Index_Request> request = queue->index_request.TryDequeue();
|
|
|
|
if (!request)
|
2017-08-17 03:39:00 +00:00
|
|
|
return false;
|
|
|
|
|
2017-08-15 05:53:44 +00:00
|
|
|
Project::Entry entry;
|
2017-08-16 03:29:49 +00:00
|
|
|
entry.filename = request->path;
|
|
|
|
entry.args = request->args;
|
2017-09-22 01:14:57 +00:00
|
|
|
std::vector<Index_DoIdMap> responses = ParseFile(
|
|
|
|
config, working_files, index, file_consumer_shared, timestamp_manager,
|
|
|
|
import_manager, request->is_interactive, entry, request->contents);
|
2017-09-13 03:35:27 +00:00
|
|
|
|
|
|
|
// Don't bother sending an IdMap request if there are no responses.
|
2017-08-17 02:14:54 +00:00
|
|
|
if (responses.empty())
|
|
|
|
return false;
|
2017-08-15 05:53:44 +00:00
|
|
|
|
2017-08-17 02:14:54 +00:00
|
|
|
// EnqueueAll will clear |responses|.
|
|
|
|
queue->do_id_map.EnqueueAll(std::move(responses));
|
|
|
|
return true;
|
2017-08-15 05:53:44 +00:00
|
|
|
}
|
|
|
|
|
2017-09-22 01:14:57 +00:00
|
|
|
bool IndexMain_DoCreateIndexUpdate(Config* config,
|
|
|
|
QueueManager* queue,
|
|
|
|
TimestampManager* timestamp_manager) {
|
2017-08-02 03:23:37 +00:00
|
|
|
optional<Index_OnIdMapped> response = queue->on_id_mapped.TryDequeue();
|
2017-07-30 04:24:02 +00:00
|
|
|
if (!response)
|
|
|
|
return false;
|
2017-04-23 20:02:41 +00:00
|
|
|
|
2017-07-30 04:24:02 +00:00
|
|
|
Timer time;
|
2017-08-02 03:23:37 +00:00
|
|
|
|
2017-07-30 18:49:24 +00:00
|
|
|
IdMap* previous_id_map = nullptr;
|
|
|
|
IndexFile* previous_index = nullptr;
|
|
|
|
if (response->previous) {
|
|
|
|
previous_id_map = response->previous->ids.get();
|
|
|
|
previous_index = response->previous->file.get();
|
|
|
|
}
|
|
|
|
|
2017-08-15 07:46:21 +00:00
|
|
|
// Build delta update.
|
2017-09-22 01:14:57 +00:00
|
|
|
IndexUpdate update =
|
|
|
|
IndexUpdate::CreateDelta(previous_id_map, response->current->ids.get(),
|
|
|
|
previous_index, response->current->file.get());
|
2017-07-30 04:24:02 +00:00
|
|
|
response->perf.index_make_delta = time.ElapsedMicrosecondsAndReset();
|
2017-09-24 00:36:28 +00:00
|
|
|
LOG_S(INFO) << "Built index update for " << response->current->file->path
|
|
|
|
<< " (is_delta=" << !!response->previous << ")";
|
2017-04-23 20:02:41 +00:00
|
|
|
|
2017-08-17 02:06:28 +00:00
|
|
|
// Write current index to disk if requested.
|
|
|
|
if (response->write_to_disk) {
|
2017-09-22 01:14:57 +00:00
|
|
|
LOG_S(INFO) << "Writing cached index to disk for "
|
|
|
|
<< response->current->file->path;
|
2017-08-15 07:46:21 +00:00
|
|
|
time.Reset();
|
|
|
|
WriteToCache(config, *response->current->file);
|
|
|
|
response->perf.index_save_to_disk = time.ElapsedMicrosecondsAndReset();
|
2017-09-22 01:14:57 +00:00
|
|
|
timestamp_manager->UpdateCachedModificationTime(
|
|
|
|
response->current->file->path,
|
|
|
|
response->current->file->last_modification_time);
|
2017-08-15 07:46:21 +00:00
|
|
|
}
|
|
|
|
|
2017-07-30 04:24:02 +00:00
|
|
|
#if false
|
2017-09-22 01:14:57 +00:00
|
|
|
#define PRINT_SECTION(name) \
|
|
|
|
if (response->perf.name) { \
|
|
|
|
total += response->perf.name; \
|
2017-07-30 04:24:02 +00:00
|
|
|
output << " " << #name << ": " << FormatMicroseconds(response->perf.name); \
|
|
|
|
}
|
|
|
|
std::stringstream output;
|
|
|
|
long long total = 0;
|
|
|
|
output << "[perf]";
|
|
|
|
PRINT_SECTION(index_parse);
|
|
|
|
PRINT_SECTION(index_build);
|
|
|
|
PRINT_SECTION(index_save_to_disk);
|
|
|
|
PRINT_SECTION(index_load_cached);
|
|
|
|
PRINT_SECTION(querydb_id_map);
|
|
|
|
PRINT_SECTION(index_make_delta);
|
|
|
|
output << "\n total: " << FormatMicroseconds(total);
|
|
|
|
output << " path: " << response->current_index->path;
|
2017-12-02 01:04:39 +00:00
|
|
|
LOG_S(INFO) << output.rdbuf();
|
2017-07-30 04:24:02 +00:00
|
|
|
#undef PRINT_SECTION
|
2017-04-23 20:02:41 +00:00
|
|
|
|
2017-07-30 04:24:02 +00:00
|
|
|
if (response->is_interactive)
|
2017-12-02 01:04:39 +00:00
|
|
|
LOG_S(INFO) << "Applying IndexUpdate" << std::endl << update.ToString();
|
2017-07-30 04:24:02 +00:00
|
|
|
#endif
|
2017-04-23 20:02:41 +00:00
|
|
|
|
2017-07-30 04:24:02 +00:00
|
|
|
Index_OnIndexed reply(update, response->perf);
|
2017-08-02 03:23:37 +00:00
|
|
|
queue->on_indexed.Enqueue(std::move(reply));
|
2017-04-23 20:02:41 +00:00
|
|
|
|
2017-07-30 04:24:02 +00:00
|
|
|
return true;
|
|
|
|
}
|
2017-04-23 20:02:41 +00:00
|
|
|
|
2017-08-15 07:22:13 +00:00
|
|
|
bool IndexMain_LoadPreviousIndex(Config* config, QueueManager* queue) {
|
|
|
|
optional<Index_DoIdMap> response = queue->load_previous_index.TryDequeue();
|
|
|
|
if (!response)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
response->previous = LoadCachedIndex(config, response->current->path);
|
2017-09-22 01:14:57 +00:00
|
|
|
LOG_IF_S(ERROR, !response->previous)
|
|
|
|
<< "Unable to load previous index for already imported index "
|
|
|
|
<< response->current->path;
|
2017-08-15 07:22:13 +00:00
|
|
|
|
|
|
|
queue->do_id_map.Enqueue(std::move(*response));
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-08-02 03:23:37 +00:00
|
|
|
bool IndexMergeIndexUpdates(QueueManager* queue) {
|
|
|
|
optional<Index_OnIndexed> root = queue->on_indexed.TryDequeue();
|
2017-07-30 04:24:02 +00:00
|
|
|
if (!root)
|
|
|
|
return false;
|
2017-04-23 20:02:41 +00:00
|
|
|
|
2017-07-30 04:24:02 +00:00
|
|
|
bool did_merge = false;
|
|
|
|
while (true) {
|
2017-08-02 03:23:37 +00:00
|
|
|
optional<Index_OnIndexed> to_join = queue->on_indexed.TryDequeue();
|
2017-07-30 04:24:02 +00:00
|
|
|
if (!to_join) {
|
2017-08-02 03:23:37 +00:00
|
|
|
queue->on_indexed.Enqueue(std::move(*root));
|
2017-07-30 04:24:02 +00:00
|
|
|
return did_merge;
|
|
|
|
}
|
2017-04-23 20:02:41 +00:00
|
|
|
|
2017-07-30 04:24:02 +00:00
|
|
|
did_merge = true;
|
2017-08-15 07:22:13 +00:00
|
|
|
Timer time;
|
2017-07-30 04:24:02 +00:00
|
|
|
root->update.Merge(to_join->update);
|
2017-09-27 06:03:43 +00:00
|
|
|
// time.ResetAndPrint("Joined querydb updates for files: " +
|
|
|
|
// StringJoinMap(root->update.files_def_update,
|
|
|
|
//[](const QueryFile::DefUpdate& update) {
|
|
|
|
// return update.path;
|
|
|
|
//}));
|
2017-07-30 04:24:02 +00:00
|
|
|
}
|
|
|
|
}
|
2017-04-23 20:02:41 +00:00
|
|
|
|
2017-09-22 01:14:57 +00:00
|
|
|
WorkThread::Result IndexMain(Config* config,
|
|
|
|
FileConsumer::SharedState* file_consumer_shared,
|
|
|
|
TimestampManager* timestamp_manager,
|
|
|
|
ImportManager* import_manager,
|
|
|
|
Project* project,
|
|
|
|
WorkingFiles* working_files,
|
|
|
|
MultiQueueWaiter* waiter,
|
|
|
|
QueueManager* queue) {
|
2017-11-26 22:20:43 +00:00
|
|
|
EmitProgress(config, queue);
|
2017-10-25 07:12:11 +00:00
|
|
|
|
2017-07-30 04:24:02 +00:00
|
|
|
// TODO: dispose of index after it is not used for a while.
|
2017-11-11 19:41:09 +00:00
|
|
|
ClangIndex index;
|
2017-04-23 20:02:41 +00:00
|
|
|
|
2017-09-13 03:35:27 +00:00
|
|
|
// TODO: process all off IndexMain_DoIndex before calling
|
|
|
|
// IndexMain_DoCreateIndexUpdate for
|
|
|
|
// better icache behavior. We need to have some threads spinning on
|
|
|
|
// both though
|
|
|
|
// otherwise memory usage will get bad.
|
|
|
|
|
|
|
|
// We need to make sure to run both IndexMain_DoParse and
|
|
|
|
// IndexMain_DoCreateIndexUpdate so we don't starve querydb from doing any
|
|
|
|
// work. Running both also lets the user query the partially constructed
|
|
|
|
// index.
|
2017-09-22 01:14:57 +00:00
|
|
|
bool did_parse =
|
|
|
|
IndexMain_DoParse(config, working_files, queue, file_consumer_shared,
|
|
|
|
timestamp_manager, import_manager, &index);
|
2017-09-13 03:35:27 +00:00
|
|
|
|
|
|
|
bool did_create_update =
|
|
|
|
IndexMain_DoCreateIndexUpdate(config, queue, timestamp_manager);
|
|
|
|
|
|
|
|
bool did_load_previous = IndexMain_LoadPreviousIndex(config, queue);
|
|
|
|
|
|
|
|
// Nothing to index and no index updates to create, so join some already
|
|
|
|
// created index updates to reduce work on querydb thread.
|
|
|
|
bool did_merge = false;
|
|
|
|
if (!did_parse && !did_create_update && !did_load_previous)
|
|
|
|
did_merge = IndexMergeIndexUpdates(queue);
|
|
|
|
|
|
|
|
// We didn't do any work, so wait for a notification.
|
|
|
|
if (!did_parse && !did_create_update && !did_merge && !did_load_previous) {
|
2017-09-22 01:14:57 +00:00
|
|
|
waiter->Wait({&queue->index_request, &queue->on_id_mapped,
|
|
|
|
&queue->load_previous_index, &queue->on_indexed});
|
2017-07-30 04:24:02 +00:00
|
|
|
}
|
2017-09-13 03:35:27 +00:00
|
|
|
|
2017-09-22 01:14:57 +00:00
|
|
|
return queue->HasWork() ? WorkThread::Result::MoreWork
|
|
|
|
: WorkThread::Result::NoWork;
|
2017-07-30 04:24:02 +00:00
|
|
|
}
|
2017-04-16 19:02:29 +00:00
|
|
|
|
2017-09-22 01:14:57 +00:00
|
|
|
bool QueryDb_ImportMain(Config* config,
|
|
|
|
QueryDatabase* db,
|
|
|
|
ImportManager* import_manager,
|
|
|
|
QueueManager* queue,
|
2017-12-04 02:23:14 +00:00
|
|
|
SemanticHighlightSymbolCache* semantic_cache,
|
2017-09-22 01:14:57 +00:00
|
|
|
WorkingFiles* working_files) {
|
2017-11-26 22:20:43 +00:00
|
|
|
EmitProgress(config, queue);
|
2017-10-25 07:12:11 +00:00
|
|
|
|
2017-08-16 03:29:49 +00:00
|
|
|
bool did_work = false;
|
|
|
|
|
|
|
|
while (true) {
|
|
|
|
optional<Index_DoIdMap> request = queue->do_id_map.TryDequeue();
|
|
|
|
if (!request)
|
|
|
|
break;
|
|
|
|
did_work = true;
|
|
|
|
|
2017-10-12 15:40:51 +00:00
|
|
|
assert(request->current);
|
|
|
|
|
2017-08-16 03:29:49 +00:00
|
|
|
// If the request does not have previous state and we have already imported
|
|
|
|
// it, load the previous state from disk and rerun IdMap logic later. Do not
|
|
|
|
// do this if we have already attempted in the past.
|
2017-09-22 01:14:57 +00:00
|
|
|
if (!request->load_previous && !request->previous &&
|
|
|
|
db->usr_to_file.find(LowerPathIfCaseInsensitive(
|
|
|
|
request->current->path)) != db->usr_to_file.end()) {
|
2017-08-16 03:29:49 +00:00
|
|
|
assert(!request->load_previous);
|
|
|
|
request->load_previous = true;
|
|
|
|
queue->load_previous_index.Enqueue(std::move(*request));
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2017-09-24 00:36:28 +00:00
|
|
|
// Check if the file is already being imported into querydb. If it is, drop
|
|
|
|
// the request.
|
|
|
|
//
|
|
|
|
// Note, we must do this *after* we have checked for the previous index,
|
|
|
|
// otherwise we will never actually generate the IdMap.
|
|
|
|
if (!import_manager->StartQueryDbImport(request->current->path)) {
|
|
|
|
LOG_S(INFO) << "Dropping index as it is already being imported for "
|
|
|
|
<< request->current->path;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2017-09-22 01:14:57 +00:00
|
|
|
Index_OnIdMapped response(request->perf, request->is_interactive,
|
|
|
|
request->write_to_disk);
|
2017-08-16 03:29:49 +00:00
|
|
|
Timer time;
|
|
|
|
|
2017-09-22 01:14:57 +00:00
|
|
|
auto make_map = [db](std::unique_ptr<IndexFile> file)
|
|
|
|
-> std::unique_ptr<Index_OnIdMapped::File> {
|
2017-08-16 03:29:49 +00:00
|
|
|
if (!file)
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
auto id_map = MakeUnique<IdMap>(db, file->id_cache);
|
2017-09-22 01:14:57 +00:00
|
|
|
return MakeUnique<Index_OnIdMapped::File>(std::move(file),
|
|
|
|
std::move(id_map));
|
2017-08-16 03:29:49 +00:00
|
|
|
};
|
|
|
|
response.current = make_map(std::move(request->current));
|
|
|
|
response.previous = make_map(std::move(request->previous));
|
|
|
|
response.perf.querydb_id_map = time.ElapsedMicrosecondsAndReset();
|
|
|
|
|
|
|
|
queue->on_id_mapped.Enqueue(std::move(response));
|
|
|
|
}
|
|
|
|
|
|
|
|
while (true) {
|
|
|
|
optional<Index_OnIndexed> response = queue->on_indexed.TryDequeue();
|
|
|
|
if (!response)
|
|
|
|
break;
|
|
|
|
|
|
|
|
did_work = true;
|
|
|
|
|
|
|
|
Timer time;
|
|
|
|
|
|
|
|
for (auto& updated_file : response->update.files_def_update) {
|
|
|
|
// TODO: We're reading a file on querydb thread. This is slow!! If this
|
|
|
|
// a real problem in practice we can load the file in a previous stage.
|
|
|
|
// It should be fine though because we only do it if the user has the
|
|
|
|
// file open.
|
2017-09-22 01:14:57 +00:00
|
|
|
WorkingFile* working_file =
|
|
|
|
working_files->GetFileByFilename(updated_file.path);
|
2017-08-16 03:29:49 +00:00
|
|
|
if (working_file) {
|
2017-09-22 01:14:57 +00:00
|
|
|
optional<std::string> cached_file_contents =
|
|
|
|
LoadCachedFileContents(config, updated_file.path);
|
2017-08-16 03:29:49 +00:00
|
|
|
if (cached_file_contents)
|
|
|
|
working_file->SetIndexContent(*cached_file_contents);
|
|
|
|
else
|
|
|
|
working_file->SetIndexContent(working_file->buffer_content);
|
2017-09-22 01:14:57 +00:00
|
|
|
time.ResetAndPrint(
|
|
|
|
"Update WorkingFile index contents (via disk load) for " +
|
|
|
|
updated_file.path);
|
2017-10-28 22:09:14 +00:00
|
|
|
|
|
|
|
// Update inactive region.
|
|
|
|
EmitInactiveLines(working_file, updated_file.inactive_regions);
|
2017-08-16 03:29:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-16 03:54:59 +00:00
|
|
|
time.Reset();
|
2017-08-16 03:29:49 +00:00
|
|
|
db->ApplyIndexUpdate(&response->update);
|
2017-09-22 01:14:57 +00:00
|
|
|
time.ResetAndPrint("Applying index update for " +
|
|
|
|
StringJoinMap(response->update.files_def_update,
|
|
|
|
[](const QueryFile::DefUpdate& value) {
|
|
|
|
return value.path;
|
|
|
|
}));
|
2017-09-24 00:36:28 +00:00
|
|
|
|
2017-11-09 07:06:32 +00:00
|
|
|
// Update semantic highlighting.
|
|
|
|
for (auto& updated_file : response->update.files_def_update) {
|
|
|
|
WorkingFile* working_file =
|
|
|
|
working_files->GetFileByFilename(updated_file.path);
|
|
|
|
if (working_file) {
|
|
|
|
QueryFileId file_id =
|
|
|
|
db->usr_to_file[LowerPathIfCaseInsensitive(working_file->filename)];
|
|
|
|
QueryFile* file = &db->files[file_id.id];
|
2017-12-04 02:23:14 +00:00
|
|
|
EmitSemanticHighlighting(db, semantic_cache, working_file, file);
|
2017-11-09 07:06:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-09-24 00:36:28 +00:00
|
|
|
// Mark the files as being done in querydb stage after we apply the index
|
|
|
|
// update.
|
|
|
|
for (auto& updated_file : response->update.files_def_update)
|
|
|
|
import_manager->DoneQueryDbImport(updated_file.path);
|
2017-08-16 03:29:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return did_work;
|
|
|
|
}
|
2017-08-15 02:07:46 +00:00
|
|
|
|
2017-09-22 01:32:55 +00:00
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// QUERYDB MAIN ////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
2017-09-22 01:14:57 +00:00
|
|
|
|
|
|
|
bool QueryDbMainLoop(Config* config,
|
|
|
|
QueryDatabase* db,
|
|
|
|
bool* exit_when_idle,
|
|
|
|
MultiQueueWaiter* waiter,
|
|
|
|
QueueManager* queue,
|
|
|
|
Project* project,
|
|
|
|
FileConsumer::SharedState* file_consumer_shared,
|
|
|
|
ImportManager* import_manager,
|
|
|
|
TimestampManager* timestamp_manager,
|
2017-12-04 02:23:14 +00:00
|
|
|
SemanticHighlightSymbolCache* semantic_cache,
|
2017-09-22 01:14:57 +00:00
|
|
|
WorkingFiles* working_files,
|
|
|
|
ClangCompleteManager* clang_complete,
|
|
|
|
IncludeComplete* include_complete,
|
|
|
|
CodeCompleteCache* global_code_complete_cache,
|
|
|
|
CodeCompleteCache* non_global_code_complete_cache,
|
|
|
|
CodeCompleteCache* signature_cache) {
|
2017-04-16 21:49:48 +00:00
|
|
|
IpcManager* ipc = IpcManager::instance();
|
2017-03-26 21:40:34 +00:00
|
|
|
|
2017-04-23 22:45:40 +00:00
|
|
|
bool did_work = false;
|
|
|
|
|
2017-09-22 01:14:57 +00:00
|
|
|
std::vector<std::unique_ptr<BaseIpcMessage>> messages =
|
2017-12-04 08:13:35 +00:00
|
|
|
ipc->for_querydb.DequeueAll();
|
2017-03-05 19:48:05 +00:00
|
|
|
for (auto& message : messages) {
|
2017-04-23 22:45:40 +00:00
|
|
|
did_work = true;
|
2017-03-05 19:48:05 +00:00
|
|
|
|
2017-12-05 07:57:41 +00:00
|
|
|
for (MessageHandler* handler : *MessageHandler::message_handlers) {
|
|
|
|
if (handler->GetId() == message->method_id) {
|
|
|
|
handler->Run(std::move(message));
|
2017-04-23 20:19:09 +00:00
|
|
|
break;
|
|
|
|
}
|
2017-12-05 07:57:41 +00:00
|
|
|
}
|
2017-12-06 03:32:33 +00:00
|
|
|
if (message) {
|
|
|
|
LOG_S(FATAL) << "Exiting; unhandled IPC message "
|
|
|
|
<< IpcIdToString(message->method_id);
|
|
|
|
exit(1);
|
2017-03-15 04:59:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-09-22 01:14:57 +00:00
|
|
|
// TODO: consider rate-limiting and checking for IPC messages so we don't
|
|
|
|
// block requests / we can serve partial requests.
|
2017-04-08 06:45:28 +00:00
|
|
|
|
2017-12-04 02:23:14 +00:00
|
|
|
if (QueryDb_ImportMain(config, db, import_manager, queue, semantic_cache,
|
|
|
|
working_files)) {
|
2017-04-23 22:45:40 +00:00
|
|
|
did_work = true;
|
2017-12-04 02:23:14 +00:00
|
|
|
}
|
2017-04-08 20:00:08 +00:00
|
|
|
|
2017-04-23 22:45:40 +00:00
|
|
|
return did_work;
|
2017-03-05 02:16:23 +00:00
|
|
|
}
|
2017-03-15 04:59:05 +00:00
|
|
|
|
2017-09-22 01:14:57 +00:00
|
|
|
void RunQueryDbThread(const std::string& bin_name,
|
|
|
|
Config* config,
|
|
|
|
MultiQueueWaiter* waiter,
|
|
|
|
QueueManager* queue) {
|
2017-09-13 03:35:27 +00:00
|
|
|
bool exit_when_idle = false;
|
2017-03-26 21:40:34 +00:00
|
|
|
Project project;
|
2017-12-04 02:23:14 +00:00
|
|
|
SemanticHighlightSymbolCache semantic_cache;
|
2017-03-26 21:40:34 +00:00
|
|
|
WorkingFiles working_files;
|
2017-09-27 06:03:43 +00:00
|
|
|
FileConsumer::SharedState file_consumer_shared;
|
|
|
|
|
2017-06-10 04:13:16 +00:00
|
|
|
ClangCompleteManager clang_complete(
|
2017-09-22 01:14:57 +00:00
|
|
|
config, &project, &working_files,
|
|
|
|
std::bind(&EmitDiagnostics, &working_files, std::placeholders::_1,
|
2017-09-27 06:03:43 +00:00
|
|
|
std::placeholders::_2),
|
|
|
|
std::bind(&IndexWithTuFromCodeCompletion, queue, &file_consumer_shared,
|
|
|
|
std::placeholders::_1, std::placeholders::_2,
|
|
|
|
std::placeholders::_3, std::placeholders::_4));
|
|
|
|
|
2017-05-27 04:21:00 +00:00
|
|
|
IncludeComplete include_complete(config, &project);
|
2017-06-30 06:51:22 +00:00
|
|
|
auto global_code_complete_cache = MakeUnique<CodeCompleteCache>();
|
|
|
|
auto non_global_code_complete_cache = MakeUnique<CodeCompleteCache>();
|
|
|
|
auto signature_cache = MakeUnique<CodeCompleteCache>();
|
2017-08-17 03:39:00 +00:00
|
|
|
ImportManager import_manager;
|
2017-08-16 03:54:59 +00:00
|
|
|
TimestampManager timestamp_manager;
|
2017-12-05 07:57:41 +00:00
|
|
|
QueryDatabase db;
|
|
|
|
|
|
|
|
// Setup shared references.
|
|
|
|
for (MessageHandler* handler : *MessageHandler::message_handlers) {
|
|
|
|
handler->config = config;
|
|
|
|
handler->db = &db;
|
|
|
|
handler->exit_when_idle = &exit_when_idle;
|
|
|
|
handler->waiter = waiter;
|
|
|
|
handler->queue = queue;
|
|
|
|
handler->project = &project;
|
|
|
|
handler->file_consumer_shared = &file_consumer_shared;
|
|
|
|
handler->import_manager = &import_manager;
|
|
|
|
handler->timestamp_manager = ×tamp_manager;
|
2017-12-06 03:32:33 +00:00
|
|
|
handler->semantic_cache = &semantic_cache;
|
2017-12-05 07:57:41 +00:00
|
|
|
handler->working_files = &working_files;
|
|
|
|
handler->clang_complete = &clang_complete;
|
|
|
|
handler->include_complete = &include_complete;
|
|
|
|
handler->global_code_complete_cache = global_code_complete_cache.get();
|
|
|
|
handler->non_global_code_complete_cache =
|
|
|
|
non_global_code_complete_cache.get();
|
|
|
|
handler->signature_cache = signature_cache.get();
|
|
|
|
}
|
2017-03-25 19:18:25 +00:00
|
|
|
|
|
|
|
// Run query db main loop.
|
2017-04-19 00:05:14 +00:00
|
|
|
SetCurrentThreadName("querydb");
|
2017-03-15 04:59:05 +00:00
|
|
|
while (true) {
|
2017-05-21 19:51:15 +00:00
|
|
|
bool did_work = QueryDbMainLoop(
|
2017-09-22 01:14:57 +00:00
|
|
|
config, &db, &exit_when_idle, waiter, queue, &project,
|
|
|
|
&file_consumer_shared, &import_manager, ×tamp_manager,
|
2017-12-04 02:23:14 +00:00
|
|
|
&semantic_cache, &working_files, &clang_complete, &include_complete,
|
2017-09-22 01:14:57 +00:00
|
|
|
global_code_complete_cache.get(), non_global_code_complete_cache.get(),
|
|
|
|
signature_cache.get());
|
2017-08-17 18:02:47 +00:00
|
|
|
|
2017-09-13 03:35:27 +00:00
|
|
|
// No more work left and exit request. Exit.
|
2017-11-18 19:02:09 +00:00
|
|
|
if (!did_work && exit_when_idle && WorkThread::num_active_threads == 0) {
|
|
|
|
LOG_S(INFO) << "Exiting; exit_when_idle is set and there is no more work";
|
2017-09-13 03:35:27 +00:00
|
|
|
exit(0);
|
2017-11-18 19:02:09 +00:00
|
|
|
}
|
2017-09-13 03:35:27 +00:00
|
|
|
|
2017-08-17 18:02:47 +00:00
|
|
|
// Cleanup and free any unused memory.
|
|
|
|
FreeUnusedMemory();
|
|
|
|
|
2017-04-23 22:45:40 +00:00
|
|
|
if (!did_work) {
|
2017-12-04 08:13:35 +00:00
|
|
|
waiter->Wait({&IpcManager::instance()->for_querydb, &queue->do_id_map,
|
|
|
|
&queue->on_indexed});
|
2017-04-23 22:45:40 +00:00
|
|
|
}
|
2017-03-15 04:59:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-09-22 01:32:55 +00:00
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// STDIN MAIN //////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
2017-03-05 19:48:05 +00:00
|
|
|
// Separate thread whose only job is to read from stdin and
|
|
|
|
// dispatch read commands to the actual indexer program. This
|
|
|
|
// cannot be done on the main thread because reading from std::cin
|
|
|
|
// blocks.
|
|
|
|
//
|
|
|
|
// |ipc| is connected to a server.
|
2017-09-22 01:14:57 +00:00
|
|
|
void LaunchStdinLoop(Config* config,
|
|
|
|
std::unordered_map<IpcId, Timer>* request_times) {
|
2017-11-17 09:30:58 +00:00
|
|
|
WorkThread::StartThread("stdin", [request_times]() {
|
2017-09-13 03:35:27 +00:00
|
|
|
IpcManager* ipc = IpcManager::instance();
|
2017-04-16 21:49:48 +00:00
|
|
|
|
2017-09-22 01:14:57 +00:00
|
|
|
std::unique_ptr<BaseIpcMessage> message =
|
2017-11-30 04:22:10 +00:00
|
|
|
MessageRegistry::instance()->ReadMessageFromStdin(
|
|
|
|
g_log_stdin_stdout_to_stderr);
|
2017-03-05 02:16:23 +00:00
|
|
|
|
|
|
|
// Message parsing can fail if we don't recognize the method.
|
|
|
|
if (!message)
|
2017-09-13 03:35:27 +00:00
|
|
|
return WorkThread::Result::MoreWork;
|
2017-03-05 02:16:23 +00:00
|
|
|
|
2017-04-21 07:46:51 +00:00
|
|
|
(*request_times)[message->method_id] = Timer();
|
|
|
|
|
2017-03-05 02:16:23 +00:00
|
|
|
switch (message->method_id) {
|
2017-09-22 01:14:57 +00:00
|
|
|
case IpcId::Initialized: {
|
|
|
|
// TODO: don't send output until we get this notification
|
|
|
|
break;
|
|
|
|
}
|
2017-03-26 06:47:59 +00:00
|
|
|
|
2017-09-22 01:14:57 +00:00
|
|
|
case IpcId::CancelRequest: {
|
|
|
|
// TODO: support cancellation
|
|
|
|
break;
|
|
|
|
}
|
2017-03-26 06:47:59 +00:00
|
|
|
|
2017-09-22 01:14:57 +00:00
|
|
|
case IpcId::Exit: {
|
2017-11-18 19:02:09 +00:00
|
|
|
LOG_S(INFO) << "Exiting";
|
2017-09-22 01:14:57 +00:00
|
|
|
exit(0);
|
|
|
|
break;
|
|
|
|
}
|
2017-09-13 03:35:27 +00:00
|
|
|
|
2017-09-22 01:14:57 +00:00
|
|
|
case IpcId::CqueryExitWhenIdle: {
|
|
|
|
// querydb needs to know to exit when idle. We return out of the stdin
|
|
|
|
// loop to exit the thread. If we keep parsing input stdin is likely
|
|
|
|
// closed so cquery will exit.
|
|
|
|
LOG_S(INFO) << "cquery will exit when all threads are idle";
|
2017-12-04 08:13:35 +00:00
|
|
|
ipc->for_querydb.Enqueue(std::move(message));
|
2017-09-22 01:14:57 +00:00
|
|
|
return WorkThread::Result::ExitThread;
|
|
|
|
}
|
2017-09-13 03:35:27 +00:00
|
|
|
|
2017-09-22 01:14:57 +00:00
|
|
|
case IpcId::Initialize:
|
|
|
|
case IpcId::TextDocumentDidOpen:
|
2017-12-03 22:35:22 +00:00
|
|
|
case IpcId::CqueryTextDocumentDidView:
|
2017-09-22 01:14:57 +00:00
|
|
|
case IpcId::TextDocumentDidChange:
|
|
|
|
case IpcId::TextDocumentDidClose:
|
|
|
|
case IpcId::TextDocumentDidSave:
|
|
|
|
case IpcId::TextDocumentRename:
|
|
|
|
case IpcId::TextDocumentCompletion:
|
|
|
|
case IpcId::TextDocumentSignatureHelp:
|
|
|
|
case IpcId::TextDocumentDefinition:
|
|
|
|
case IpcId::TextDocumentDocumentHighlight:
|
|
|
|
case IpcId::TextDocumentHover:
|
|
|
|
case IpcId::TextDocumentReferences:
|
|
|
|
case IpcId::TextDocumentDocumentSymbol:
|
|
|
|
case IpcId::TextDocumentDocumentLink:
|
|
|
|
case IpcId::TextDocumentCodeAction:
|
|
|
|
case IpcId::TextDocumentCodeLens:
|
|
|
|
case IpcId::WorkspaceSymbol:
|
|
|
|
case IpcId::CqueryFreshenIndex:
|
|
|
|
case IpcId::CqueryTypeHierarchyTree:
|
|
|
|
case IpcId::CqueryCallTreeInitial:
|
|
|
|
case IpcId::CqueryCallTreeExpand:
|
|
|
|
case IpcId::CqueryVars:
|
|
|
|
case IpcId::CqueryCallers:
|
|
|
|
case IpcId::CqueryBase:
|
|
|
|
case IpcId::CqueryDerived:
|
|
|
|
case IpcId::CqueryIndexFile:
|
|
|
|
case IpcId::CqueryQueryDbWaitForIdleIndexer: {
|
2017-12-04 08:13:35 +00:00
|
|
|
ipc->for_querydb.Enqueue(std::move(message));
|
2017-09-22 01:14:57 +00:00
|
|
|
break;
|
|
|
|
}
|
2017-03-26 06:47:59 +00:00
|
|
|
|
2017-09-22 01:14:57 +00:00
|
|
|
default: {
|
2017-11-18 19:02:09 +00:00
|
|
|
LOG_S(ERROR) << "Unhandled IPC message "
|
|
|
|
<< IpcIdToString(message->method_id);
|
2017-09-22 01:14:57 +00:00
|
|
|
exit(1);
|
|
|
|
}
|
2017-03-03 08:12:11 +00:00
|
|
|
}
|
2017-09-13 03:35:27 +00:00
|
|
|
|
|
|
|
return WorkThread::Result::MoreWork;
|
|
|
|
});
|
2017-03-03 08:12:11 +00:00
|
|
|
}
|
|
|
|
|
2017-09-22 01:14:57 +00:00
|
|
|
void LaunchStdoutThread(std::unordered_map<IpcId, Timer>* request_times,
|
|
|
|
MultiQueueWaiter* waiter,
|
|
|
|
QueueManager* queue) {
|
2017-09-13 03:35:27 +00:00
|
|
|
WorkThread::StartThread("stdout", [=]() {
|
|
|
|
IpcManager* ipc = IpcManager::instance();
|
2017-04-16 19:02:29 +00:00
|
|
|
|
2017-12-04 08:13:35 +00:00
|
|
|
std::vector<IpcManager::StdoutMessage> messages =
|
|
|
|
ipc->for_stdout.DequeueAll();
|
2017-04-23 22:45:40 +00:00
|
|
|
if (messages.empty()) {
|
2017-12-04 08:13:35 +00:00
|
|
|
waiter->Wait({&ipc->for_stdout});
|
2017-09-22 01:14:57 +00:00
|
|
|
return queue->HasWork() ? WorkThread::Result::MoreWork
|
|
|
|
: WorkThread::Result::NoWork;
|
2017-04-23 22:45:40 +00:00
|
|
|
}
|
2017-04-16 19:02:29 +00:00
|
|
|
|
2017-04-23 22:45:40 +00:00
|
|
|
for (auto& message : messages) {
|
2017-12-04 08:13:35 +00:00
|
|
|
if (ShouldDisplayIpcTiming(message.id)) {
|
|
|
|
Timer time = (*request_times)[message.id];
|
|
|
|
time.ResetAndPrint("[e2e] Running " +
|
|
|
|
std::string(IpcIdToString(message.id)));
|
|
|
|
}
|
2017-04-23 22:45:40 +00:00
|
|
|
|
2017-12-04 08:13:35 +00:00
|
|
|
if (g_log_stdin_stdout_to_stderr) {
|
|
|
|
std::ostringstream sstream;
|
|
|
|
sstream << "[COUT] |";
|
|
|
|
sstream << message.content;
|
|
|
|
sstream << "|\n";
|
|
|
|
std::cerr << sstream.str();
|
|
|
|
std::cerr.flush();
|
2017-04-16 23:57:31 +00:00
|
|
|
}
|
2017-12-04 08:13:35 +00:00
|
|
|
|
|
|
|
std::cout << message.content;
|
|
|
|
std::cout.flush();
|
2017-04-16 23:57:31 +00:00
|
|
|
}
|
2017-09-13 03:35:27 +00:00
|
|
|
|
|
|
|
return WorkThread::Result::MoreWork;
|
|
|
|
});
|
2017-04-16 23:57:31 +00:00
|
|
|
}
|
2017-04-16 19:02:29 +00:00
|
|
|
|
2017-09-22 01:14:57 +00:00
|
|
|
void LanguageServerMain(const std::string& bin_name,
|
|
|
|
Config* config,
|
|
|
|
MultiQueueWaiter* waiter) {
|
2017-09-13 03:35:27 +00:00
|
|
|
QueueManager queue(waiter);
|
2017-04-21 07:46:51 +00:00
|
|
|
std::unordered_map<IpcId, Timer> request_times;
|
|
|
|
|
Fix hang during reading from stdin
I experienced this hang problem when using cquery with
LanguageClient-neovim.
Sometimes std::cout would hang because the pipe is full, which would
normally be fine, since the client would read from the pipe soon.
However, in this case the client is blocking on a write(). This
shouldn't happen, because cquery has a stdin thread which constantly
reads from stdin. But, in C++, cin and cout are tied streams. Reading
from cin would cause cout to flush, which cause the read to block.
So, cquery can't write because the client doesn't read, the client won't
read before it finishes writing. It can't finish writing because cquery
can't read, and cquery can't read because cquery can't write. Which is a
deadlock.
The solution is to simply untie cin and cout.
2017-11-28 18:17:50 +00:00
|
|
|
std::cin.tie(NULL);
|
2017-09-13 03:35:27 +00:00
|
|
|
LaunchStdinLoop(config, &request_times);
|
2017-04-23 20:19:09 +00:00
|
|
|
|
|
|
|
// We run a dedicated thread for writing to stdout because there can be an
|
|
|
|
// unknown number of delays when output information.
|
2017-09-13 03:35:27 +00:00
|
|
|
LaunchStdoutThread(&request_times, waiter, &queue);
|
|
|
|
|
|
|
|
// Start querydb which takes over this thread. The querydb will launch
|
|
|
|
// indexer threads as needed.
|
|
|
|
RunQueryDbThread(bin_name, config, waiter, &queue);
|
2017-03-16 07:36:49 +00:00
|
|
|
}
|
2017-03-03 08:12:11 +00:00
|
|
|
|
2017-09-22 01:32:55 +00:00
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// MAIN ////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
2017-03-25 01:28:09 +00:00
|
|
|
int main(int argc, char** argv) {
|
2017-12-06 18:34:26 +00:00
|
|
|
std::unordered_map<std::string, std::string> options =
|
|
|
|
ParseOptions(argc, argv);
|
|
|
|
|
|
|
|
if (!HasOption(options, "--log-all-to-stderr"))
|
|
|
|
loguru::g_stderr_verbosity = loguru::Verbosity_WARNING;
|
|
|
|
|
2017-07-28 02:14:33 +00:00
|
|
|
loguru::g_flush_interval_ms = 0;
|
2017-12-06 18:34:26 +00:00
|
|
|
loguru::init(argc, argv);
|
2017-07-28 02:14:33 +00:00
|
|
|
|
2017-04-23 22:45:40 +00:00
|
|
|
MultiQueueWaiter waiter;
|
|
|
|
IpcManager::CreateInstance(&waiter);
|
2017-04-16 22:48:54 +00:00
|
|
|
|
2017-09-22 01:14:57 +00:00
|
|
|
// bool loop = true;
|
|
|
|
// while (loop)
|
2017-04-03 01:34:15 +00:00
|
|
|
// std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
2017-09-22 01:14:57 +00:00
|
|
|
// std::this_thread::sleep_for(std::chrono::seconds(10));
|
2017-03-03 08:12:11 +00:00
|
|
|
|
2017-03-25 20:27:28 +00:00
|
|
|
PlatformInit();
|
2017-04-16 21:51:47 +00:00
|
|
|
IndexInit();
|
|
|
|
|
2017-11-19 22:35:16 +00:00
|
|
|
bool print_help = true;
|
|
|
|
|
2017-12-06 18:34:26 +00:00
|
|
|
if (HasOption(options, "--log-file")) {
|
|
|
|
loguru::add_file(options["--log-file"].c_str(), loguru::Truncate,
|
|
|
|
loguru::Verbosity_MAX);
|
2017-12-01 17:45:44 +00:00
|
|
|
}
|
|
|
|
|
2017-11-30 04:22:10 +00:00
|
|
|
if (HasOption(options, "--log-stdin-stdout-to-stderr"))
|
|
|
|
g_log_stdin_stdout_to_stderr = true;
|
|
|
|
|
2017-12-06 18:34:26 +00:00
|
|
|
if (HasOption(options, "--clang-sanity-check")) {
|
|
|
|
print_help = false;
|
|
|
|
ClangSanityCheck();
|
|
|
|
}
|
|
|
|
|
2017-11-19 22:35:16 +00:00
|
|
|
if (HasOption(options, "--test-unit")) {
|
|
|
|
print_help = false;
|
2017-03-17 23:45:10 +00:00
|
|
|
doctest::Context context;
|
|
|
|
context.applyCommandLine(argc, argv);
|
|
|
|
int res = context.run();
|
|
|
|
if (context.shouldExit())
|
2017-03-25 19:18:25 +00:00
|
|
|
return res;
|
2017-11-19 22:35:16 +00:00
|
|
|
}
|
2017-03-17 23:45:10 +00:00
|
|
|
|
2017-11-19 22:35:16 +00:00
|
|
|
if (HasOption(options, "--test-index")) {
|
|
|
|
print_help = false;
|
|
|
|
RunIndexTests();
|
2017-11-28 02:27:16 +00:00
|
|
|
#if defined(_WIN32)
|
2017-05-25 02:04:19 +00:00
|
|
|
std::cerr << std::endl << "[Enter] to exit" << std::endl;
|
|
|
|
std::cin.get();
|
2017-11-28 02:27:16 +00:00
|
|
|
#endif
|
2017-11-19 22:35:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (HasOption(options, "--language-server")) {
|
|
|
|
print_help = false;
|
2017-09-22 01:14:57 +00:00
|
|
|
// std::cerr << "Running language server" << std::endl;
|
2017-09-24 00:36:28 +00:00
|
|
|
auto config = MakeUnique<Config>();
|
|
|
|
LanguageServerMain(argv[0], config.get(), &waiter);
|
2017-04-23 19:45:58 +00:00
|
|
|
return 0;
|
2017-11-19 22:35:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (print_help) {
|
2017-12-06 18:34:26 +00:00
|
|
|
std::cout << R"help(cquery is a low-latency C++ language server.
|
|
|
|
|
|
|
|
Command line options:
|
|
|
|
--language-server
|
|
|
|
Run as a language server. This implements the language server
|
|
|
|
spec over STDIN and STDOUT.
|
|
|
|
--test-unit Run unit tests.
|
|
|
|
--test-index Run index tests.
|
|
|
|
--log-stdin-stdout-to-stderr
|
|
|
|
Print stdin and stdout messages to stderr. This is a aid for
|
|
|
|
developing new language clients, as it makes it easier to figure
|
|
|
|
out how the client is interacting with cquery.
|
|
|
|
--log-file <absoulte_path>
|
|
|
|
Emit diagnostic logging to the given path, which is taken
|
|
|
|
literally, ie, it will be relative to the current working
|
|
|
|
directory.
|
|
|
|
--log-all-to-stderr
|
|
|
|
Write all log messages to STDERR.
|
|
|
|
--clang-sanity-check
|
|
|
|
Run a simple index test. Verifies basic clang functionality.
|
|
|
|
Needs to be executed from the cquery root checkout directory.
|
|
|
|
--help Print this help information.
|
|
|
|
|
|
|
|
Configuration:
|
|
|
|
When opening up a directory, cquery will look for a compile_commands.json file
|
|
|
|
emitted by your preferred build system. If not present, cquery will use a
|
|
|
|
recursive directory listing instead. Command line flags can be provided by
|
|
|
|
adding a file named `.cquery` in the top-level directory. Each line in that
|
|
|
|
file is a separate argument.
|
|
|
|
|
|
|
|
There are also a number of configuration options available when initializing
|
|
|
|
the language server - your editor should have tooling to describe those
|
|
|
|
options. See |Config| in this source code for a detailed list of all
|
|
|
|
currently supported options.
|
2017-03-25 19:18:25 +00:00
|
|
|
)help";
|
2017-02-25 23:59:09 +00:00
|
|
|
}
|
2017-11-19 22:35:16 +00:00
|
|
|
|
|
|
|
return 0;
|
2017-02-25 23:59:09 +00:00
|
|
|
}
|