mirror of
https://github.com/MaskRay/ccls.git
synced 2024-11-22 07:35:08 +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
|
* diagnostics
|
||||||
* code actions (clang FixIts)
|
* code actions (clang FixIts)
|
||||||
* darken/fade code disabled by preprocessor
|
* 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
|
# 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/sparsehash/src
|
||||||
-IC:/Users/jacob/Desktop/superindex/indexer/third_party/sparsepp
|
-IC:/Users/jacob/Desktop/superindex/indexer/third_party/sparsepp
|
||||||
-IC:/Program Files/LLVM/include
|
-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
|
# OSX
|
||||||
#-I/Users/jdufault/Personal/super-clang-index/third_party
|
#-I/Users/jdufault/Personal/super-clang-index/third_party
|
||||||
#-I/Users/jdufault/Personal/super-clang-index/third_party/doctest
|
#-I/Users/jdufault/Personal/super-clang-index/third_party/doctest
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
#include "options.h"
|
#include "options.h"
|
||||||
#include "project.h"
|
#include "project.h"
|
||||||
#include "platform.h"
|
#include "platform.h"
|
||||||
|
#include "standard_includes.h"
|
||||||
#include "test.h"
|
#include "test.h"
|
||||||
#include "timer.h"
|
#include "timer.h"
|
||||||
#include "threaded_queue.h"
|
#include "threaded_queue.h"
|
||||||
@ -21,6 +22,7 @@
|
|||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <iterator>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
@ -55,14 +57,14 @@ const int kExpectedClientVersion = 1;
|
|||||||
|
|
||||||
|
|
||||||
std::string FormatMicroseconds(long long microseconds) {
|
std::string FormatMicroseconds(long long microseconds) {
|
||||||
long long milliseconds = microseconds / 1000;
|
long long milliseconds = microseconds / 1000;
|
||||||
long long remaining = microseconds - milliseconds;
|
long long remaining = microseconds - milliseconds;
|
||||||
|
|
||||||
// Only show two digits after the dot.
|
// Only show two digits after the dot.
|
||||||
while (remaining >= 100)
|
while (remaining >= 100)
|
||||||
remaining /= 10;
|
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
|
// the user erases a character. vscode will resend the completion request if
|
||||||
// that happens.
|
// that happens.
|
||||||
struct CodeCompleteCache {
|
struct CodeCompleteCache {
|
||||||
optional<std::string> cached_path;
|
optional<std::string> cached_path;
|
||||||
optional<lsPosition> cached_completion_position;
|
optional<lsPosition> cached_completion_position;
|
||||||
NonElidedVector<lsCompletionItem> cached_results;
|
NonElidedVector<lsCompletionItem> cached_results;
|
||||||
NonElidedVector<lsDiagnostic> cached_diagnostics;
|
NonElidedVector<lsDiagnostic> cached_diagnostics;
|
||||||
|
|
||||||
bool IsCacheValid(lsTextDocumentPositionParams position) const {
|
bool IsCacheValid(lsTextDocumentPositionParams position) const {
|
||||||
return cached_path == position.textDocument.uri.GetPath() &&
|
return cached_path == position.textDocument.uri.GetPath() &&
|
||||||
cached_completion_position == position.position;
|
cached_completion_position == position.position;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -103,26 +105,26 @@ struct CodeCompleteCache {
|
|||||||
|
|
||||||
|
|
||||||
struct IpcManager {
|
struct IpcManager {
|
||||||
static IpcManager* instance_;
|
static IpcManager* instance_;
|
||||||
static IpcManager* instance() {
|
static IpcManager* instance() {
|
||||||
return instance_;
|
return instance_;
|
||||||
}
|
}
|
||||||
static void CreateInstance(MultiQueueWaiter* waiter) {
|
static void CreateInstance(MultiQueueWaiter* waiter) {
|
||||||
instance_ = new IpcManager(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_client_;
|
||||||
std::unique_ptr<ThreadedQueue<std::unique_ptr<BaseIpcMessage>>> threaded_queue_for_server_;
|
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) {
|
||||||
return destination == Destination::Client ? threaded_queue_for_client_.get() : threaded_queue_for_server_.get();
|
return destination == Destination::Client ? threaded_queue_for_client_.get() : threaded_queue_for_server_.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SendOutMessageToClient(IpcId id, lsBaseOutMessage& response) {
|
void SendOutMessageToClient(IpcId id, lsBaseOutMessage& response) {
|
||||||
std::ostringstream sstream;
|
std::ostringstream sstream;
|
||||||
response.Write(sstream);
|
response.Write(sstream);
|
||||||
|
|
||||||
@ -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 += '/';
|
config->cacheDirectory += '/';
|
||||||
MakeDirectoryRecursive(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.
|
// Start indexer threads.
|
||||||
int indexer_count = std::max<int>(std::thread::hardware_concurrency(), 2) - 1;
|
int indexer_count = std::max<int>(std::thread::hardware_concurrency(), 2) - 1;
|
||||||
if (config->indexerCount > 0)
|
if (config->indexerCount > 0)
|
||||||
@ -1520,7 +1586,7 @@ bool QueryDbMainLoop(
|
|||||||
response.result.capabilities.completionProvider->resolveProvider = false;
|
response.result.capabilities.completionProvider->resolveProvider = false;
|
||||||
// vscode doesn't support trigger character sequences, so we use ':' for '::' and '>' for '->'.
|
// vscode doesn't support trigger character sequences, so we use ':' for '::' and '>' for '->'.
|
||||||
// See https://github.com/Microsoft/language-server-protocol/issues/138.
|
// 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();
|
response.result.capabilities.signatureHelpProvider = lsSignatureHelpOptions();
|
||||||
// NOTE: If updating signature help tokens make sure to also update
|
// NOTE: If updating signature help tokens make sure to also update
|
||||||
@ -1772,6 +1838,78 @@ bool QueryDbMainLoop(
|
|||||||
lsTextDocumentPositionParams& params = msg->params;
|
lsTextDocumentPositionParams& params = msg->params;
|
||||||
|
|
||||||
WorkingFile* file = working_files->GetFileByFilename(params.textDocument.uri.GetPath());
|
WorkingFile* file = working_files->GetFileByFilename(params.textDocument.uri.GetPath());
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
else {
|
||||||
if (file)
|
if (file)
|
||||||
params.position = file->FindStableCompletionSource(params.position);
|
params.position = file->FindStableCompletionSource(params.position);
|
||||||
|
|
||||||
@ -1814,6 +1952,7 @@ bool QueryDbMainLoop(
|
|||||||
else {
|
else {
|
||||||
completion_manager->CodeComplete(params, std::move(callback));
|
completion_manager->CodeComplete(params, std::move(callback));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -1957,7 +2096,6 @@ bool QueryDbMainLoop(
|
|||||||
for (const IndexInclude& include : file->def.includes) {
|
for (const IndexInclude& include : file->def.includes) {
|
||||||
if (include.line == target_line) {
|
if (include.line == target_line) {
|
||||||
lsLocation result;
|
lsLocation result;
|
||||||
std::cerr << "!! resolved to " << include.resolved_path << std::endl;
|
|
||||||
result.uri = lsDocumentUri::FromPath(include.resolved_path);
|
result.uri = lsDocumentUri::FromPath(include.resolved_path);
|
||||||
response.result.push_back(result);
|
response.result.push_back(result);
|
||||||
break;
|
break;
|
||||||
@ -2158,7 +2296,6 @@ bool QueryDbMainLoop(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
response.Write(std::cerr);
|
|
||||||
ipc->SendOutMessageToClient(IpcId::TextDocumentDocumentLink, response);
|
ipc->SendOutMessageToClient(IpcId::TextDocumentDocumentLink, response);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -56,6 +56,9 @@ void Reflect(Reader& visitor, lsRequestId& id);
|
|||||||
|
|
||||||
|
|
||||||
struct IndexerConfig {
|
struct IndexerConfig {
|
||||||
|
// Root directory of the project. **Not serialized**
|
||||||
|
std::string projectRoot;
|
||||||
|
|
||||||
std::string cacheDirectory;
|
std::string cacheDirectory;
|
||||||
NonElidedVector<std::string> whitelist;
|
NonElidedVector<std::string> whitelist;
|
||||||
NonElidedVector<std::string> blacklist;
|
NonElidedVector<std::string> blacklist;
|
||||||
@ -397,6 +400,19 @@ struct lsTextDocumentPositionParams {
|
|||||||
};
|
};
|
||||||
MAKE_REFLECT_STRUCT(lsTextDocumentPositionParams, textDocument, position);
|
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
|
// Defines whether the insert text in a completion item should be interpreted as
|
||||||
// plain text or a snippet.
|
// plain text or a snippet.
|
||||||
enum class lsInsertTextFormat {
|
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
|
// *Note:* The range of the edit must be a single line range and it must contain the position at which completion
|
||||||
// has been requested.
|
// has been requested.
|
||||||
// TextEdit 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
|
||||||
// selecting this completion. Edits must not overlap with the main edit
|
// selecting this completion. Edits must not overlap with the main edit
|
||||||
@ -502,7 +518,8 @@ MAKE_REFLECT_STRUCT(lsCompletionItem,
|
|||||||
documentation,
|
documentation,
|
||||||
sortText,
|
sortText,
|
||||||
insertText,
|
insertText,
|
||||||
insertTextFormat);
|
insertTextFormat,
|
||||||
|
textEdit);
|
||||||
|
|
||||||
|
|
||||||
struct lsTextDocumentItem {
|
struct lsTextDocumentItem {
|
||||||
@ -521,19 +538,6 @@ struct lsTextDocumentItem {
|
|||||||
};
|
};
|
||||||
MAKE_REFLECT_STRUCT(lsTextDocumentItem, uri, languageId, version, text);
|
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 {
|
struct lsTextDocumentEdit {
|
||||||
// The text document to change.
|
// The text document to change.
|
||||||
lsVersionedTextDocumentIdentifier textDocument;
|
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
|
// 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[] = {
|
||||||
"-isystem",
|
|
||||||
"-I",
|
"-I",
|
||||||
"-iquote",
|
"-iquote",
|
||||||
|
"-isystem",
|
||||||
"--sysroot="
|
"--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;
|
Project::Entry result;
|
||||||
result.filename = NormalizePath(entry.file);
|
result.filename = NormalizePath(entry.file);
|
||||||
|
|
||||||
bool make_next_flag_absolute = false;
|
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());
|
result.args.reserve(entry.args.size() + extra_flags.size());
|
||||||
for (size_t i = 0; i < entry.args.size(); ++i) {
|
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] != '/')
|
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)
|
||||||
|
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.
|
// Update arg if it is a path.
|
||||||
for (const char* flag_type : kPathArgs) {
|
for (const char* flag_type : kPathArgs) {
|
||||||
if (arg == flag_type) {
|
if (arg == flag_type) {
|
||||||
make_next_flag_absolute = true;
|
make_next_flag_absolute = true;
|
||||||
|
add_next_flag_quote = ShouldAddToQuoteIncludes(arg);
|
||||||
|
add_next_flag_angle = ShouldAddToAngleIncludes(arg);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (StartsWith(arg, flag_type)) {
|
if (StartsWith(arg, flag_type)) {
|
||||||
std::string path = arg.substr(strlen(flag_type));
|
std::string path = arg.substr(strlen(flag_type));
|
||||||
if (path.size() > 0 && path[0] != '/') {
|
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;
|
arg = flag_type + path;
|
||||||
}
|
}
|
||||||
|
if (ShouldAddToQuoteIncludes(arg))
|
||||||
|
quote_includes.insert(path);
|
||||||
|
if (ShouldAddToAngleIncludes(arg))
|
||||||
|
angle_includes.insert(path);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (make_next_flag_absolute)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
result.args.push_back(arg);
|
result.args.push_back(arg);
|
||||||
}
|
}
|
||||||
@ -154,7 +195,9 @@ Project::Entry GetCompilationEntryFromCompileCommandEntry(const std::vector<std:
|
|||||||
return result;
|
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.
|
// 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");
|
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())
|
if (entry.args.empty() && !entry.command.empty())
|
||||||
entry.args = SplitString(entry.command, " ");
|
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;
|
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<Project::Entry> result;
|
||||||
|
|
||||||
std::vector<std::string> args;
|
std::vector<std::string> args;
|
||||||
@ -193,27 +238,25 @@ std::vector<Project::Entry> LoadFromDirectoryListing(const std::vector<std::stri
|
|||||||
std::cerr << line;
|
std::cerr << line;
|
||||||
args.push_back(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::cerr << std::endl;
|
||||||
|
|
||||||
|
|
||||||
std::vector<std::string> files = GetFilesInFolder(project_directory, true /*recursive*/, true /*add_folder_to_path*/);
|
std::vector<std::string> files = GetFilesInFolder(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")) {
|
||||||
Project::Entry entry;
|
CompileCommandsEntry e;
|
||||||
entry.filename = NormalizePath(file);
|
e.file = NormalizePath(file);
|
||||||
entry.args = args;
|
e.args = args;
|
||||||
result.push_back(entry);
|
result.push_back(GetCompilationEntryFromCompileCommandEntry(quote_includes, angle_includes, extra_flags, e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
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.
|
// TODO: Figure out if this function or the clang one is faster.
|
||||||
//return LoadFromCompileCommandsJson(extra_flags, project_directory);
|
//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);
|
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 \"" << 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);
|
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)
|
for (unsigned i = 0; i < num_args; ++i)
|
||||||
entry.args.push_back(clang::ToString(clang_CompileCommand_getArg(cx_command, 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);
|
clang_CompileCommands_dispose(cx_commands);
|
||||||
@ -278,10 +321,30 @@ int ComputeGuessScore(const std::string& a, const std::string& b) {
|
|||||||
return score;
|
return score;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EnsureEndsInSlash(std::string& path) {
|
||||||
|
if (path.empty() || path[path.size() - 1] != '/')
|
||||||
|
path += '/';
|
||||||
|
}
|
||||||
|
|
||||||
} // 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) {
|
||||||
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());
|
absolute_path_to_entry_index_.resize(entries.size());
|
||||||
for (int i = 0; i < entries.size(); ++i)
|
for (int i = 0; i < entries.size(); ++i)
|
||||||
|
@ -21,6 +21,11 @@ struct Project {
|
|||||||
bool is_inferred = false;
|
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;
|
std::vector<Entry> entries;
|
||||||
spp::sparse_hash_map<std::string, int> absolute_path_to_entry_index_;
|
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