mirror of
https://github.com/MaskRay/ccls.git
synced 2024-11-21 23:25:07 +00:00
Add #include auto-complete.
This commit is contained in:
parent
7a79532fff
commit
94bd6fc301
@ -28,7 +28,7 @@ be productive with cquery. Here's a list of implemented features:
|
||||
* diagnostics
|
||||
* code actions (clang FixIts)
|
||||
* darken/fade code disabled by preprocessor
|
||||
* goto definition, document links on include to jump to file
|
||||
* #include auto-complete and quick-jump (goto definition, document links)
|
||||
|
||||
# Setup - build cquery, install extension, setup project
|
||||
|
||||
|
@ -10,6 +10,10 @@
|
||||
-IC:/Users/jacob/Desktop/superindex/indexer/third_party/sparsehash/src
|
||||
-IC:/Users/jacob/Desktop/superindex/indexer/third_party/sparsepp
|
||||
-IC:/Program Files/LLVM/include
|
||||
-IC:/Program Files (x86)/Windows Kits/10/Include/10.0.14393.0
|
||||
-IC:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/include
|
||||
|
||||
|
||||
# OSX
|
||||
#-I/Users/jdufault/Personal/super-clang-index/third_party
|
||||
#-I/Users/jdufault/Personal/super-clang-index/third_party/doctest
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include "options.h"
|
||||
#include "project.h"
|
||||
#include "platform.h"
|
||||
#include "standard_includes.h"
|
||||
#include "test.h"
|
||||
#include "timer.h"
|
||||
#include "threaded_queue.h"
|
||||
@ -21,6 +22,7 @@
|
||||
#include <fstream>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <iterator>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <unordered_map>
|
||||
@ -55,14 +57,14 @@ const int kExpectedClientVersion = 1;
|
||||
|
||||
|
||||
std::string FormatMicroseconds(long long microseconds) {
|
||||
long long milliseconds = microseconds / 1000;
|
||||
long long remaining = microseconds - milliseconds;
|
||||
long long milliseconds = microseconds / 1000;
|
||||
long long remaining = microseconds - milliseconds;
|
||||
|
||||
// Only show two digits after the dot.
|
||||
while (remaining >= 100)
|
||||
remaining /= 10;
|
||||
// Only show two digits after the dot.
|
||||
while (remaining >= 100)
|
||||
remaining /= 10;
|
||||
|
||||
return std::to_string(milliseconds) + "." + std::to_string(remaining) + "ms";
|
||||
return std::to_string(milliseconds) + "." + std::to_string(remaining) + "ms";
|
||||
}
|
||||
|
||||
|
||||
@ -83,15 +85,15 @@ std::string FormatMicroseconds(long long microseconds) {
|
||||
// the user erases a character. vscode will resend the completion request if
|
||||
// that happens.
|
||||
struct CodeCompleteCache {
|
||||
optional<std::string> cached_path;
|
||||
optional<lsPosition> cached_completion_position;
|
||||
NonElidedVector<lsCompletionItem> cached_results;
|
||||
NonElidedVector<lsDiagnostic> cached_diagnostics;
|
||||
optional<std::string> cached_path;
|
||||
optional<lsPosition> cached_completion_position;
|
||||
NonElidedVector<lsCompletionItem> cached_results;
|
||||
NonElidedVector<lsDiagnostic> cached_diagnostics;
|
||||
|
||||
bool IsCacheValid(lsTextDocumentPositionParams position) const {
|
||||
return cached_path == position.textDocument.uri.GetPath() &&
|
||||
cached_completion_position == position.position;
|
||||
}
|
||||
bool IsCacheValid(lsTextDocumentPositionParams position) const {
|
||||
return cached_path == position.textDocument.uri.GetPath() &&
|
||||
cached_completion_position == position.position;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -103,27 +105,27 @@ struct CodeCompleteCache {
|
||||
|
||||
|
||||
struct IpcManager {
|
||||
static IpcManager* instance_;
|
||||
static IpcManager* instance() {
|
||||
return instance_;
|
||||
}
|
||||
static void CreateInstance(MultiQueueWaiter* waiter) {
|
||||
instance_ = new IpcManager(waiter);
|
||||
}
|
||||
static IpcManager* instance_;
|
||||
static IpcManager* instance() {
|
||||
return instance_;
|
||||
}
|
||||
static void CreateInstance(MultiQueueWaiter* waiter) {
|
||||
instance_ = new IpcManager(waiter);
|
||||
}
|
||||
|
||||
std::unique_ptr<ThreadedQueue<std::unique_ptr<BaseIpcMessage>>> threaded_queue_for_client_;
|
||||
std::unique_ptr<ThreadedQueue<std::unique_ptr<BaseIpcMessage>>> threaded_queue_for_server_;
|
||||
std::unique_ptr<ThreadedQueue<std::unique_ptr<BaseIpcMessage>>> threaded_queue_for_client_;
|
||||
std::unique_ptr<ThreadedQueue<std::unique_ptr<BaseIpcMessage>>> threaded_queue_for_server_;
|
||||
|
||||
enum class Destination {
|
||||
Client, Server
|
||||
};
|
||||
enum class Destination {
|
||||
Client, Server
|
||||
};
|
||||
|
||||
ThreadedQueue<std::unique_ptr<BaseIpcMessage>>* GetThreadedQueue(Destination destination) {
|
||||
return destination == Destination::Client ? threaded_queue_for_client_.get() : threaded_queue_for_server_.get();
|
||||
}
|
||||
ThreadedQueue<std::unique_ptr<BaseIpcMessage>>* GetThreadedQueue(Destination destination) {
|
||||
return destination == Destination::Client ? threaded_queue_for_client_.get() : threaded_queue_for_server_.get();
|
||||
}
|
||||
|
||||
void SendOutMessageToClient(IpcId id, lsBaseOutMessage& response) {
|
||||
std::ostringstream sstream;
|
||||
void SendOutMessageToClient(IpcId id, lsBaseOutMessage& response) {
|
||||
std::ostringstream sstream;
|
||||
response.Write(sstream);
|
||||
|
||||
auto out = MakeUnique<Ipc_Cout>();
|
||||
@ -177,11 +179,70 @@ bool ShouldDisplayIpcTiming(IpcId id) {
|
||||
|
||||
|
||||
|
||||
std::string BaseName(const std::string& path) {
|
||||
int i = path.size() - 1;
|
||||
while (i > 0) {
|
||||
char c = path[i - 1];
|
||||
if (c == '/' || c == '\\')
|
||||
break;
|
||||
--i;
|
||||
}
|
||||
return path.substr(i);
|
||||
}
|
||||
|
||||
int TrimCommonPathPrefix(const std::string& result, const std::string& trimmer) {
|
||||
size_t i = 0;
|
||||
while (i < result.size() && i < trimmer.size()) {
|
||||
char a = result[i];
|
||||
char b = trimmer[i];
|
||||
#if defined(_WIN32)
|
||||
a = tolower(a);
|
||||
b = tolower(b);
|
||||
#endif
|
||||
if (a != b)
|
||||
break;
|
||||
++i;
|
||||
}
|
||||
|
||||
if (i == trimmer.size())
|
||||
return i;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// Returns true iff angle brackets should be used.
|
||||
bool TrimPath(Project* project, const std::string& project_root, std::string& insert_path) {
|
||||
int start = 0;
|
||||
bool angle = false;
|
||||
|
||||
int len = TrimCommonPathPrefix(insert_path, project_root);
|
||||
if (len > start)
|
||||
start = len;
|
||||
|
||||
for (auto& include_dir : project->quote_include_directories) {
|
||||
len = TrimCommonPathPrefix(insert_path, include_dir);
|
||||
if (len > start)
|
||||
start = len;
|
||||
}
|
||||
|
||||
for (auto& include_dir : project->angle_include_directories) {
|
||||
len = TrimCommonPathPrefix(insert_path, include_dir);
|
||||
if (len > start) {
|
||||
start = len;
|
||||
angle = true;
|
||||
}
|
||||
}
|
||||
|
||||
insert_path = insert_path.substr(start);
|
||||
return angle;
|
||||
}
|
||||
|
||||
bool ShouldRunIncludeCompletion(const std::string& line) {
|
||||
size_t start = 0;
|
||||
while (start < line.size() && isspace(line[start]))
|
||||
++start;
|
||||
return start < line.size() && line[start] == '#';
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -1477,6 +1538,11 @@ bool QueryDbMainLoop(
|
||||
config->cacheDirectory += '/';
|
||||
MakeDirectoryRecursive(config->cacheDirectory);
|
||||
|
||||
// Set project root.
|
||||
config->projectRoot = NormalizePath(request->params.rootUri->GetPath());
|
||||
if (config->projectRoot.empty() || config->projectRoot[config->projectRoot.size() - 1] != '/')
|
||||
config->projectRoot += '/';
|
||||
|
||||
// Start indexer threads.
|
||||
int indexer_count = std::max<int>(std::thread::hardware_concurrency(), 2) - 1;
|
||||
if (config->indexerCount > 0)
|
||||
@ -1520,7 +1586,7 @@ bool QueryDbMainLoop(
|
||||
response.result.capabilities.completionProvider->resolveProvider = false;
|
||||
// vscode doesn't support trigger character sequences, so we use ':' for '::' and '>' for '->'.
|
||||
// See https://github.com/Microsoft/language-server-protocol/issues/138.
|
||||
response.result.capabilities.completionProvider->triggerCharacters = { ".", ":", ">" };
|
||||
response.result.capabilities.completionProvider->triggerCharacters = { ".", ":", ">", "#" };
|
||||
|
||||
response.result.capabilities.signatureHelpProvider = lsSignatureHelpOptions();
|
||||
// NOTE: If updating signature help tokens make sure to also update
|
||||
@ -1772,47 +1838,120 @@ bool QueryDbMainLoop(
|
||||
lsTextDocumentPositionParams& params = msg->params;
|
||||
|
||||
WorkingFile* file = working_files->GetFileByFilename(params.textDocument.uri.GetPath());
|
||||
if (file)
|
||||
params.position = file->FindStableCompletionSource(params.position);
|
||||
|
||||
CompletionManager::OnComplete callback = std::bind([working_files, code_complete_cache](BaseIpcMessage* message, NonElidedVector<lsCompletionItem> results, NonElidedVector<lsDiagnostic> diagnostics) {
|
||||
auto msg = static_cast<Ipc_TextDocumentComplete*>(message);
|
||||
auto ipc = IpcManager::instance();
|
||||
// TODO: We should scan include directories to add any missing paths
|
||||
|
||||
std::string buffer_line = file->all_buffer_lines[params.position.line];
|
||||
|
||||
if (ShouldRunIncludeCompletion(buffer_line)) {
|
||||
Out_TextDocumentComplete complete_response;
|
||||
complete_response.id = msg->id;
|
||||
complete_response.result.isIncomplete = false;
|
||||
complete_response.result.items = results;
|
||||
|
||||
// Emit completion results.
|
||||
for (const char* stl_header : kStandardLibraryIncludes) {
|
||||
lsCompletionItem item;
|
||||
item.label = "#include <" + std::string(stl_header) + ">";
|
||||
item.insertTextFormat = lsInsertTextFormat::PlainText;
|
||||
item.kind = lsCompletionItemKind::Module;
|
||||
|
||||
// Replace the entire existing content.
|
||||
item.textEdit = lsTextEdit();
|
||||
item.textEdit->range.start.line = params.position.line;
|
||||
item.textEdit->range.start.character = 0;
|
||||
item.textEdit->range.end.line = params.position.line;
|
||||
item.textEdit->range.end.character = buffer_line.size();
|
||||
item.textEdit->newText = item.label;
|
||||
|
||||
complete_response.result.items.push_back(item);
|
||||
}
|
||||
|
||||
for (optional<QueryFile>& file : db->files) {
|
||||
if (!file)
|
||||
continue;
|
||||
|
||||
// TODO: codify list of file extensions somewhere
|
||||
if (EndsWith(file->def.path, ".c") ||
|
||||
EndsWith(file->def.path, ".cc") ||
|
||||
EndsWith(file->def.path, ".cpp"))
|
||||
continue;
|
||||
|
||||
lsCompletionItem item;
|
||||
|
||||
// Standard library headers are handled differently.
|
||||
std::string base_name = BaseName(file->def.path);
|
||||
if (std::find(std::begin(kStandardLibraryIncludes), std::end(kStandardLibraryIncludes), base_name) != std::end(kStandardLibraryIncludes))
|
||||
continue;
|
||||
|
||||
// Trim the path so it is relative to an include directory.
|
||||
std::string insert_path = file->def.path;
|
||||
std::string start_quote = "\"";
|
||||
std::string end_quote = "\"";
|
||||
if (TrimPath(project, config->projectRoot, insert_path)) {
|
||||
start_quote = "<";
|
||||
end_quote = ">";
|
||||
}
|
||||
|
||||
item.label = "#include " + start_quote + insert_path + end_quote;
|
||||
item.insertTextFormat = lsInsertTextFormat::PlainText;
|
||||
item.kind = lsCompletionItemKind::File;
|
||||
|
||||
// Replace the entire existing content.
|
||||
item.textEdit = lsTextEdit();
|
||||
item.textEdit->range.start.line = params.position.line;
|
||||
item.textEdit->range.start.character = 0;
|
||||
item.textEdit->range.end.line = params.position.line;
|
||||
item.textEdit->range.end.character = buffer_line.size();
|
||||
item.textEdit->newText = item.label;
|
||||
|
||||
complete_response.result.items.push_back(item);
|
||||
}
|
||||
|
||||
complete_response.Write(std::cerr);
|
||||
ipc->SendOutMessageToClient(IpcId::TextDocumentCompletion, complete_response);
|
||||
|
||||
// Emit diagnostics.
|
||||
Out_TextDocumentPublishDiagnostics diagnostic_response;
|
||||
diagnostic_response.params.uri = msg->params.textDocument.uri;
|
||||
diagnostic_response.params.diagnostics = diagnostics;
|
||||
ipc->SendOutMessageToClient(IpcId::TextDocumentPublishDiagnostics, diagnostic_response);
|
||||
|
||||
// Cache diagnostics so we can show fixits.
|
||||
WorkingFile* working_file = working_files->GetFileByFilename(msg->params.textDocument.uri.GetPath());
|
||||
if (working_file)
|
||||
working_file->diagnostics = diagnostics;
|
||||
|
||||
// Cache completion results so if the user types backspace we can respond faster.
|
||||
code_complete_cache->cached_path = msg->params.textDocument.uri.GetPath();
|
||||
code_complete_cache->cached_completion_position = msg->params.position;
|
||||
code_complete_cache->cached_results = results;
|
||||
code_complete_cache->cached_diagnostics = diagnostics;
|
||||
|
||||
delete message;
|
||||
}, message.release(), std::placeholders::_1, std::placeholders::_2);
|
||||
|
||||
if (code_complete_cache->IsCacheValid(params)) {
|
||||
std::cerr << "[complete] Using cached completion results at " << params.position.ToString() << std::endl;
|
||||
callback(code_complete_cache->cached_results, code_complete_cache->cached_diagnostics);
|
||||
}
|
||||
else {
|
||||
completion_manager->CodeComplete(params, std::move(callback));
|
||||
if (file)
|
||||
params.position = file->FindStableCompletionSource(params.position);
|
||||
|
||||
CompletionManager::OnComplete callback = std::bind([working_files, code_complete_cache](BaseIpcMessage* message, NonElidedVector<lsCompletionItem> results, NonElidedVector<lsDiagnostic> diagnostics) {
|
||||
auto msg = static_cast<Ipc_TextDocumentComplete*>(message);
|
||||
auto ipc = IpcManager::instance();
|
||||
|
||||
Out_TextDocumentComplete complete_response;
|
||||
complete_response.id = msg->id;
|
||||
complete_response.result.isIncomplete = false;
|
||||
complete_response.result.items = results;
|
||||
|
||||
// Emit completion results.
|
||||
ipc->SendOutMessageToClient(IpcId::TextDocumentCompletion, complete_response);
|
||||
|
||||
// Emit diagnostics.
|
||||
Out_TextDocumentPublishDiagnostics diagnostic_response;
|
||||
diagnostic_response.params.uri = msg->params.textDocument.uri;
|
||||
diagnostic_response.params.diagnostics = diagnostics;
|
||||
ipc->SendOutMessageToClient(IpcId::TextDocumentPublishDiagnostics, diagnostic_response);
|
||||
|
||||
// Cache diagnostics so we can show fixits.
|
||||
WorkingFile* working_file = working_files->GetFileByFilename(msg->params.textDocument.uri.GetPath());
|
||||
if (working_file)
|
||||
working_file->diagnostics = diagnostics;
|
||||
|
||||
// Cache completion results so if the user types backspace we can respond faster.
|
||||
code_complete_cache->cached_path = msg->params.textDocument.uri.GetPath();
|
||||
code_complete_cache->cached_completion_position = msg->params.position;
|
||||
code_complete_cache->cached_results = results;
|
||||
code_complete_cache->cached_diagnostics = diagnostics;
|
||||
|
||||
delete message;
|
||||
}, message.release(), std::placeholders::_1, std::placeholders::_2);
|
||||
|
||||
if (code_complete_cache->IsCacheValid(params)) {
|
||||
std::cerr << "[complete] Using cached completion results at " << params.position.ToString() << std::endl;
|
||||
callback(code_complete_cache->cached_results, code_complete_cache->cached_diagnostics);
|
||||
}
|
||||
else {
|
||||
completion_manager->CodeComplete(params, std::move(callback));
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
@ -1957,7 +2096,6 @@ bool QueryDbMainLoop(
|
||||
for (const IndexInclude& include : file->def.includes) {
|
||||
if (include.line == target_line) {
|
||||
lsLocation result;
|
||||
std::cerr << "!! resolved to " << include.resolved_path << std::endl;
|
||||
result.uri = lsDocumentUri::FromPath(include.resolved_path);
|
||||
response.result.push_back(result);
|
||||
break;
|
||||
@ -2158,7 +2296,6 @@ bool QueryDbMainLoop(
|
||||
}
|
||||
}
|
||||
|
||||
response.Write(std::cerr);
|
||||
ipc->SendOutMessageToClient(IpcId::TextDocumentDocumentLink, response);
|
||||
break;
|
||||
}
|
||||
|
@ -56,6 +56,9 @@ void Reflect(Reader& visitor, lsRequestId& id);
|
||||
|
||||
|
||||
struct IndexerConfig {
|
||||
// Root directory of the project. **Not serialized**
|
||||
std::string projectRoot;
|
||||
|
||||
std::string cacheDirectory;
|
||||
NonElidedVector<std::string> whitelist;
|
||||
NonElidedVector<std::string> blacklist;
|
||||
@ -397,6 +400,19 @@ struct lsTextDocumentPositionParams {
|
||||
};
|
||||
MAKE_REFLECT_STRUCT(lsTextDocumentPositionParams, textDocument, position);
|
||||
|
||||
struct lsTextEdit {
|
||||
// The range of the text document to be manipulated. To insert
|
||||
// text into a document create a range where start === end.
|
||||
lsRange range;
|
||||
|
||||
// The string to be inserted. For delete operations use an
|
||||
// empty string.
|
||||
std::string newText;
|
||||
|
||||
bool operator==(const lsTextEdit& that);
|
||||
};
|
||||
MAKE_REFLECT_STRUCT(lsTextEdit, range, newText);
|
||||
|
||||
// Defines whether the insert text in a completion item should be interpreted as
|
||||
// plain text or a snippet.
|
||||
enum class lsInsertTextFormat {
|
||||
@ -479,7 +495,7 @@ struct lsCompletionItem {
|
||||
//
|
||||
// *Note:* The range of the edit must be a single line range and it must contain the position at which completion
|
||||
// has been requested.
|
||||
// TextEdit textEdit;
|
||||
optional<lsTextEdit> textEdit;
|
||||
|
||||
// An optional array of additional text edits that are applied when
|
||||
// selecting this completion. Edits must not overlap with the main edit
|
||||
@ -502,7 +518,8 @@ MAKE_REFLECT_STRUCT(lsCompletionItem,
|
||||
documentation,
|
||||
sortText,
|
||||
insertText,
|
||||
insertTextFormat);
|
||||
insertTextFormat,
|
||||
textEdit);
|
||||
|
||||
|
||||
struct lsTextDocumentItem {
|
||||
@ -521,19 +538,6 @@ struct lsTextDocumentItem {
|
||||
};
|
||||
MAKE_REFLECT_STRUCT(lsTextDocumentItem, uri, languageId, version, text);
|
||||
|
||||
struct lsTextEdit {
|
||||
// The range of the text document to be manipulated. To insert
|
||||
// text into a document create a range where start === end.
|
||||
lsRange range;
|
||||
|
||||
// The string to be inserted. For delete operations use an
|
||||
// empty string.
|
||||
std::string newText;
|
||||
|
||||
bool operator==(const lsTextEdit& that);
|
||||
};
|
||||
MAKE_REFLECT_STRUCT(lsTextEdit, range, newText);
|
||||
|
||||
struct lsTextDocumentEdit {
|
||||
// The text document to change.
|
||||
lsVersionedTextDocumentIdentifier textDocument;
|
||||
|
103
src/project.cc
103
src/project.cc
@ -82,17 +82,44 @@ static const char *kBlacklist[] = {
|
||||
// Arguments which are followed by a potentially relative path. We need to make
|
||||
// all relative paths absolute, otherwise libclang will not resolve them.
|
||||
const char* kPathArgs[] = {
|
||||
"-isystem",
|
||||
"-I",
|
||||
"-iquote",
|
||||
"-isystem",
|
||||
"--sysroot="
|
||||
};
|
||||
|
||||
Project::Entry GetCompilationEntryFromCompileCommandEntry(const std::vector<std::string>& extra_flags, const CompileCommandsEntry& entry) {
|
||||
const char* kQuoteIncludeArgs[] = {
|
||||
"-iquote"
|
||||
};
|
||||
const char* kAngleIncludeArgs[] = {
|
||||
"-I",
|
||||
"-isystem"
|
||||
};
|
||||
|
||||
bool ShouldAddToQuoteIncludes(const std::string& arg) {
|
||||
for (const char* flag_type : kQuoteIncludeArgs) {
|
||||
if (arg == flag_type)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool ShouldAddToAngleIncludes(const std::string& arg) {
|
||||
for (const char* flag_type : kAngleIncludeArgs) {
|
||||
if (StartsWith(arg, flag_type))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Project::Entry GetCompilationEntryFromCompileCommandEntry(
|
||||
std::unordered_set<std::string>& quote_includes, std::unordered_set<std::string>& angle_includes,
|
||||
const std::vector<std::string>& extra_flags, const CompileCommandsEntry& entry) {
|
||||
Project::Entry result;
|
||||
result.filename = NormalizePath(entry.file);
|
||||
|
||||
bool make_next_flag_absolute = false;
|
||||
bool add_next_flag_quote = false;
|
||||
bool add_next_flag_angle = false;
|
||||
|
||||
result.args.reserve(entry.args.size() + extra_flags.size());
|
||||
for (size_t i = 0; i < entry.args.size(); ++i) {
|
||||
@ -116,26 +143,40 @@ Project::Entry GetCompilationEntryFromCompileCommandEntry(const std::vector<std:
|
||||
if (arg.size() > 0 && arg[0] != '/')
|
||||
arg = NormalizePath(entry.directory + arg);
|
||||
make_next_flag_absolute = false;
|
||||
|
||||
if (add_next_flag_quote)
|
||||
quote_includes.insert(arg);
|
||||
if (add_next_flag_angle)
|
||||
angle_includes.insert(arg);
|
||||
add_next_flag_quote = false;
|
||||
add_next_flag_angle = false;
|
||||
}
|
||||
|
||||
// Update arg if it is a path.
|
||||
for (const char* flag_type : kPathArgs) {
|
||||
if (arg == flag_type) {
|
||||
make_next_flag_absolute = true;
|
||||
add_next_flag_quote = ShouldAddToQuoteIncludes(arg);
|
||||
add_next_flag_angle = ShouldAddToAngleIncludes(arg);
|
||||
break;
|
||||
}
|
||||
|
||||
if (StartsWith(arg, flag_type)) {
|
||||
std::string path = arg.substr(strlen(flag_type));
|
||||
if (path.size() > 0 && path[0] != '/') {
|
||||
path = NormalizePath(entry.directory + "/" + path);
|
||||
if (!entry.directory.empty())
|
||||
path = entry.directory + "/" + path;
|
||||
path = NormalizePath(path);
|
||||
|
||||
arg = flag_type + path;
|
||||
}
|
||||
if (ShouldAddToQuoteIncludes(arg))
|
||||
quote_includes.insert(path);
|
||||
if (ShouldAddToAngleIncludes(arg))
|
||||
angle_includes.insert(path);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (make_next_flag_absolute)
|
||||
continue;
|
||||
|
||||
result.args.push_back(arg);
|
||||
}
|
||||
@ -154,7 +195,9 @@ Project::Entry GetCompilationEntryFromCompileCommandEntry(const std::vector<std:
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<Project::Entry> LoadFromCompileCommandsJson(const std::vector<std::string>& extra_flags, const std::string& project_directory) {
|
||||
std::vector<Project::Entry> LoadFromCompileCommandsJson(
|
||||
std::unordered_set<std::string>& quote_includes, std::unordered_set<std::string>& angle_includes,
|
||||
const std::vector<std::string>& extra_flags, const std::string& project_directory) {
|
||||
// TODO: Fix this function, it may be way faster than libclang's implementation.
|
||||
|
||||
optional<std::string> compile_commands_content = ReadContent(project_directory + "/compile_commands.json");
|
||||
@ -175,12 +218,14 @@ std::vector<Project::Entry> LoadFromCompileCommandsJson(const std::vector<std::s
|
||||
if (entry.args.empty() && !entry.command.empty())
|
||||
entry.args = SplitString(entry.command, " ");
|
||||
|
||||
result.push_back(GetCompilationEntryFromCompileCommandEntry(extra_flags, entry));
|
||||
result.push_back(GetCompilationEntryFromCompileCommandEntry(quote_includes, angle_includes, extra_flags, entry));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<Project::Entry> LoadFromDirectoryListing(const std::vector<std::string>& extra_flags, const std::string& project_directory) {
|
||||
std::vector<Project::Entry> LoadFromDirectoryListing(
|
||||
std::unordered_set<std::string>& quote_includes, 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<std::string> args;
|
||||
@ -193,27 +238,25 @@ std::vector<Project::Entry> LoadFromDirectoryListing(const std::vector<std::stri
|
||||
std::cerr << line;
|
||||
args.push_back(line);
|
||||
}
|
||||
for (const std::string& flag : extra_flags) {
|
||||
std::cerr << flag << std::endl;
|
||||
args.push_back(flag);
|
||||
}
|
||||
std::cerr << std::endl;
|
||||
|
||||
|
||||
std::vector<std::string> files = GetFilesInFolder(project_directory, true /*recursive*/, true /*add_folder_to_path*/);
|
||||
for (const std::string& file : files) {
|
||||
if (EndsWith(file, ".cc") || EndsWith(file, ".cpp") || EndsWith(file, ".c")) {
|
||||
Project::Entry entry;
|
||||
entry.filename = NormalizePath(file);
|
||||
entry.args = args;
|
||||
result.push_back(entry);
|
||||
CompileCommandsEntry e;
|
||||
e.file = NormalizePath(file);
|
||||
e.args = args;
|
||||
result.push_back(GetCompilationEntryFromCompileCommandEntry(quote_includes, angle_includes, extra_flags, e));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<Project::Entry> LoadCompilationEntriesFromDirectory(const std::vector<std::string>& extra_flags, const std::string& project_directory) {
|
||||
std::vector<Project::Entry> LoadCompilationEntriesFromDirectory(
|
||||
std::unordered_set<std::string>& quote_includes, 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.
|
||||
//return LoadFromCompileCommandsJson(extra_flags, project_directory);
|
||||
|
||||
@ -222,7 +265,7 @@ std::vector<Project::Entry> LoadCompilationEntriesFromDirectory(const std::vecto
|
||||
CXCompilationDatabase cx_db = clang_CompilationDatabase_fromDirectory(project_directory.c_str(), &cx_db_load_error);
|
||||
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;
|
||||
return LoadFromDirectoryListing(extra_flags, project_directory);
|
||||
return LoadFromDirectoryListing(quote_includes, angle_includes, extra_flags, project_directory);
|
||||
}
|
||||
|
||||
CXCompileCommands cx_commands = clang_CompilationDatabase_getAllCompileCommands(cx_db);
|
||||
@ -245,7 +288,7 @@ std::vector<Project::Entry> LoadCompilationEntriesFromDirectory(const std::vecto
|
||||
for (unsigned i = 0; i < num_args; ++i)
|
||||
entry.args.push_back(clang::ToString(clang_CompileCommand_getArg(cx_command, i)));
|
||||
|
||||
result.push_back(GetCompilationEntryFromCompileCommandEntry(extra_flags, entry));
|
||||
result.push_back(GetCompilationEntryFromCompileCommandEntry(quote_includes, angle_includes, extra_flags, entry));
|
||||
}
|
||||
|
||||
clang_CompileCommands_dispose(cx_commands);
|
||||
@ -278,10 +321,30 @@ int ComputeGuessScore(const std::string& a, const std::string& b) {
|
||||
return score;
|
||||
}
|
||||
|
||||
void EnsureEndsInSlash(std::string& path) {
|
||||
if (path.empty() || path[path.size() - 1] != '/')
|
||||
path += '/';
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void Project::Load(const std::vector<std::string>& extra_flags, const std::string& directory) {
|
||||
entries = LoadCompilationEntriesFromDirectory(extra_flags, directory);
|
||||
std::unordered_set<std::string> unique_quote_includes;
|
||||
std::unordered_set<std::string> unique_angle_includes;
|
||||
|
||||
entries = LoadCompilationEntriesFromDirectory(unique_quote_includes, unique_angle_includes, extra_flags, directory);
|
||||
|
||||
quote_include_directories.assign(unique_quote_includes.begin(), unique_quote_includes.end());
|
||||
angle_include_directories.assign(unique_angle_includes.begin(), unique_angle_includes.end());
|
||||
|
||||
for (std::string& path : quote_include_directories) {
|
||||
EnsureEndsInSlash(path);
|
||||
std::cerr << "quote_include_dir: " << path << std::endl;
|
||||
}
|
||||
for (std::string& path : angle_include_directories) {
|
||||
EnsureEndsInSlash(path);
|
||||
std::cerr << "angle_include_dir: " << path << std::endl;
|
||||
}
|
||||
|
||||
absolute_path_to_entry_index_.resize(entries.size());
|
||||
for (int i = 0; i < entries.size(); ++i)
|
||||
|
@ -21,6 +21,11 @@ struct Project {
|
||||
bool is_inferred = false;
|
||||
};
|
||||
|
||||
// Include directories for "" headers
|
||||
std::vector<std::string> quote_include_directories;
|
||||
// Include directories for <> headers
|
||||
std::vector<std::string> angle_include_directories;
|
||||
|
||||
std::vector<Entry> entries;
|
||||
spp::sparse_hash_map<std::string, int> absolute_path_to_entry_index_;
|
||||
|
||||
|
182
src/standard_includes.cc
Normal file
182
src/standard_includes.cc
Normal file
@ -0,0 +1,182 @@
|
||||
#include "standard_includes.h"
|
||||
|
||||
// See http://stackoverflow.com/a/2029106.
|
||||
const char* kStandardLibraryIncludes[177] = {
|
||||
"aio.h",
|
||||
"algorithm",
|
||||
"any",
|
||||
"arpa/inet.h",
|
||||
"array",
|
||||
"assert.h",
|
||||
"atomic",
|
||||
"bitset",
|
||||
"cassert",
|
||||
"ccomplex",
|
||||
"cctype",
|
||||
"cerrno",
|
||||
"cfenv",
|
||||
"cfloat",
|
||||
"chrono",
|
||||
"cinttypes",
|
||||
"ciso646",
|
||||
"climits",
|
||||
"clocale",
|
||||
"cmath",
|
||||
"codecvt",
|
||||
"complex",
|
||||
"complex.h",
|
||||
"condition_variable",
|
||||
"cpio.h",
|
||||
"csetjmp",
|
||||
"csignal",
|
||||
"cstdalign",
|
||||
"cstdarg",
|
||||
"cstdbool",
|
||||
"cstddef",
|
||||
"cstdint",
|
||||
"cstdio",
|
||||
"cstdlib",
|
||||
"cstring",
|
||||
"ctgmath",
|
||||
"ctime",
|
||||
"ctype.h",
|
||||
"cuchar",
|
||||
"curses.h",
|
||||
"cwchar",
|
||||
"cwctype",
|
||||
"deque",
|
||||
"dirent.h",
|
||||
"dlfcn.h",
|
||||
"errno.h",
|
||||
"exception",
|
||||
"execution",
|
||||
"fcntl.h",
|
||||
"fenv.h",
|
||||
"filesystem",
|
||||
"float.h",
|
||||
"fmtmsg.h",
|
||||
"fnmatch.h",
|
||||
"forward_list",
|
||||
"fstream",
|
||||
"ftw.h",
|
||||
"functional",
|
||||
"future",
|
||||
"glob.h",
|
||||
"grp.h",
|
||||
"iconv.h",
|
||||
"initializer_list",
|
||||
"inttypes.h",
|
||||
"iomanip",
|
||||
"ios",
|
||||
"iosfwd",
|
||||
"iostream",
|
||||
"iso646.h",
|
||||
"istream",
|
||||
"iterator",
|
||||
"langinfo.h",
|
||||
"libgen.h",
|
||||
"limits",
|
||||
"limits.h",
|
||||
"list",
|
||||
"locale",
|
||||
"locale.h",
|
||||
"map",
|
||||
"math.h",
|
||||
"memory",
|
||||
"memory_resource",
|
||||
"monetary.h",
|
||||
"mqueue.h",
|
||||
"mutex",
|
||||
"ndbm.h",
|
||||
"net/if.h",
|
||||
"netdb.h",
|
||||
"netinet/in.h",
|
||||
"netinet/tcp.h",
|
||||
"new",
|
||||
"nl_types.h",
|
||||
"numeric",
|
||||
"optional",
|
||||
"ostream",
|
||||
"poll.h",
|
||||
"pthread.h",
|
||||
"pwd.h",
|
||||
"queue",
|
||||
"random",
|
||||
"ratio",
|
||||
"regex",
|
||||
"regex.h",
|
||||
"sched.h",
|
||||
"scoped_allocator",
|
||||
"search.h",
|
||||
"semaphore.h",
|
||||
"set",
|
||||
"setjmp.h",
|
||||
"shared_mutex",
|
||||
"signal.h",
|
||||
"spawn.h",
|
||||
"sstream",
|
||||
"stack",
|
||||
"stdalign.h",
|
||||
"stdarg.h",
|
||||
"stdatomic.h",
|
||||
"stdbool.h",
|
||||
"stddef.h",
|
||||
"stdexcept",
|
||||
"stdint.h",
|
||||
"stdio.h",
|
||||
"stdlib.h",
|
||||
"stdnoreturn.h",
|
||||
"streambuf",
|
||||
"string",
|
||||
"string.h",
|
||||
"string_view",
|
||||
"strings.h",
|
||||
"stropts.h",
|
||||
"strstream",
|
||||
"sys/ipc.h",
|
||||
"sys/mman.h",
|
||||
"sys/msg.h",
|
||||
"sys/resource.h",
|
||||
"sys/select.h",
|
||||
"sys/sem.h",
|
||||
"sys/shm.h",
|
||||
"sys/socket.h",
|
||||
"sys/stat.h",
|
||||
"sys/statvfs.h",
|
||||
"sys/time.h",
|
||||
"sys/times.h",
|
||||
"sys/types.h",
|
||||
"sys/uio.h",
|
||||
"sys/un.h",
|
||||
"sys/utsname.h",
|
||||
"sys/wait.h",
|
||||
"syslog.h",
|
||||
"system_error",
|
||||
"tar.h",
|
||||
"term.h",
|
||||
"termios.h",
|
||||
"tgmath.h",
|
||||
"thread",
|
||||
"threads.h",
|
||||
"time.h",
|
||||
"trace.h",
|
||||
"tuple",
|
||||
"type_traits",
|
||||
"typeindex",
|
||||
"typeinfo",
|
||||
"uchar.h",
|
||||
"ulimit.h",
|
||||
"uncntrl.h",
|
||||
"unistd.h",
|
||||
"unordered_map",
|
||||
"unordered_set",
|
||||
"utility",
|
||||
"utime.h",
|
||||
"utmpx.h",
|
||||
"valarray",
|
||||
"variant",
|
||||
"vector",
|
||||
"wchar.h",
|
||||
"wctype.h",
|
||||
"wordexp.h",
|
||||
};
|
4
src/standard_includes.h
Normal file
4
src/standard_includes.h
Normal file
@ -0,0 +1,4 @@
|
||||
#pragma once
|
||||
|
||||
// A set of standard libary header names, ie, "vector".
|
||||
extern const char* kStandardLibraryIncludes[177];
|
Loading…
Reference in New Issue
Block a user