Skip to content

Commit

Permalink
Extend .ccls
Browse files Browse the repository at this point in the history
* Add %h for C header files (the suffix .h is considered a C header, not a C++ header)
* Add %hpp for C++ header files
* If .ccls exists, it provides full command line for files not specified by compile_commands.json (before, compile_commands.json was ignored)
* If the first line of .ccls is %compile_commands.json, it appends flags to compile_commands.json "arguments", instead of overriding.
  Files not specified by compile_commands.json will not be added to folder.entries, but their command line can be inferred from other files.

Also fix `#include <` completion of -I flags for clang < 8
  • Loading branch information
MaskRay committed Dec 22, 2018
1 parent 698a0c6 commit ddcbc2c
Show file tree
Hide file tree
Showing 7 changed files with 278 additions and 284 deletions.
118 changes: 46 additions & 72 deletions src/include_complete.cc
Expand Up @@ -61,39 +61,26 @@ size_t TrimCommonPathPrefix(const std::string &result,
}

// Returns true iff angle brackets should be used.
bool TrimPath(Project *project, std::string &path) {
int TrimPath(Project *project, std::string &path) {
size_t pos = 0;
bool angle = false;
for (auto &[root, folder] : project->root2folder) {
size_t pos1 = 0;
for (auto &search : folder.angle_search_list)
pos1 = std::max(pos1, TrimCommonPathPrefix(path, search));
if (pos1 > pos) {
pos = pos1;
angle = true;
}

pos1 = TrimCommonPathPrefix(path, root);
for (auto &search : folder.quote_search_list)
pos1 = std::max(pos1, TrimCommonPathPrefix(path, search));
if (pos1 > pos) {
pos = pos1;
angle = false;
}
}
int kind = 0;
for (auto &[root, folder] : project->root2folder)
for (auto &[search, kind1] : folder.search_dir2kind)
if (int t = TrimCommonPathPrefix(path, search); t > pos)
pos = t, kind = kind1;
path = path.substr(pos);
return angle;
return kind;
}

CompletionItem BuildCompletionItem(const std::string &path,
bool use_angle_brackets) {
int kind) {
CompletionItem item;
item.label = ElideLongPath(path);
item.detail = path; // the include path, used in de-duplicating
item.textEdit.newText = path;
item.insertTextFormat = InsertTextFormat::PlainText;
item.use_angle_brackets_ = use_angle_brackets;
item.kind = CompletionItemKind::File;
item.quote_kind_ = kind;
item.priority_ = 0;
return item;
}
Expand Down Expand Up @@ -125,14 +112,41 @@ void IncludeComplete::Rescan() {
is_scanning = true;
std::thread([this]() {
set_thread_name("include");
std::unordered_set<std::string> angle_set, quote_set;
for (auto &[root, folder] : project_->root2folder) {
for (const std::string &search : folder.angle_search_list)
if (angle_set.insert(search).second)
InsertIncludesFromDirectory(search, true);
for (const std::string &search : folder.quote_search_list)
if (quote_set.insert(search).second)
InsertIncludesFromDirectory(search, false);
for (auto &search_kind : folder.search_dir2kind) {
const std::string &search = search_kind.first;
int kind = search_kind.second;
assert(search.back() == '/');
if (match_ && !match_->Matches(search))
return;
bool include_cpp = search.find("include/c++") != std::string::npos;

std::vector<CompletionCandidate> results;
GetFilesInFolder(search, true /*recursive*/,
false /*add_folder_to_path*/,
[&](const std::string &path) {
bool ok = include_cpp;
for (StringRef suffix :
g_config->completion.include.suffixWhitelist)
if (StringRef(path).endswith(suffix))
ok = true;
if (!ok)
return;
if (match_ && !match_->Matches(search + path))
return;

CompletionCandidate candidate;
candidate.absolute_path = search + path;
candidate.completion_item =
BuildCompletionItem(path, kind);
results.push_back(candidate);
});

std::lock_guard lock(completion_items_mutex);
for (CompletionCandidate &result : results)
InsertCompletionItem(result.absolute_path,
std::move(result.completion_item));
}
}

is_scanning = false;
Expand All @@ -142,7 +156,7 @@ void IncludeComplete::Rescan() {

void IncludeComplete::InsertCompletionItem(const std::string &absolute_path,
CompletionItem &&item) {
if (inserted_paths.insert({item.detail, inserted_paths.size()}).second) {
if (inserted_paths.try_emplace(item.detail, inserted_paths.size()).second) {
completion_items.push_back(item);
// insert if not found or with shorter include path
auto it = absolute_path_to_completion_item.find(absolute_path);
Expand All @@ -151,12 +165,6 @@ void IncludeComplete::InsertCompletionItem(const std::string &absolute_path,
absolute_path_to_completion_item[absolute_path] =
completion_items.size() - 1;
}
} else {
CompletionItem &inserted_item =
completion_items[inserted_paths[item.detail]];
// Update |use_angle_brackets_|, prefer quotes.
if (!item.use_angle_brackets_)
inserted_item.use_angle_brackets_ = false;
}
}

Expand All @@ -171,49 +179,15 @@ void IncludeComplete::AddFile(const std::string &path) {
return;

std::string trimmed_path = path;
bool use_angle_brackets = TrimPath(project_, trimmed_path);
CompletionItem item = BuildCompletionItem(trimmed_path, use_angle_brackets);
int kind = TrimPath(project_, trimmed_path);
CompletionItem item = BuildCompletionItem(trimmed_path, kind);

std::unique_lock<std::mutex> lock(completion_items_mutex, std::defer_lock);
if (is_scanning)
lock.lock();
InsertCompletionItem(path, std::move(item));
}

void IncludeComplete::InsertIncludesFromDirectory(std::string directory,
bool use_angle_brackets) {
directory = NormalizePath(directory);
EnsureEndsInSlash(directory);
if (match_ && !match_->Matches(directory))
return;
bool include_cpp = directory.find("include/c++") != std::string::npos;

std::vector<CompletionCandidate> results;
GetFilesInFolder(
directory, true /*recursive*/, false /*add_folder_to_path*/,
[&](const std::string &path) {
bool ok = include_cpp;
for (StringRef suffix : g_config->completion.include.suffixWhitelist)
if (StringRef(path).endswith(suffix))
ok = true;
if (!ok)
return;
if (match_ && !match_->Matches(directory + path))
return;

CompletionCandidate candidate;
candidate.absolute_path = directory + path;
candidate.completion_item =
BuildCompletionItem(path, use_angle_brackets);
results.push_back(candidate);
});

std::lock_guard<std::mutex> lock(completion_items_mutex);
for (CompletionCandidate &result : results)
InsertCompletionItem(result.absolute_path,
std::move(result.completion_item));
}

std::optional<CompletionItem>
IncludeComplete::FindCompletionItemForAbsolutePath(
const std::string &absolute_path) {
Expand Down
5 changes: 0 additions & 5 deletions src/include_complete.hh
Expand Up @@ -34,11 +34,6 @@ struct IncludeComplete {
// Ensures the one-off file is inside |completion_items|.
void AddFile(const std::string &absolute_path);

// Scans the given directory and inserts all includes from this. This is a
// blocking function and should be run off the querydb thread.
void InsertIncludesFromDirectory(std::string directory,
bool use_angle_brackets);

std::optional<ccls::CompletionItem>
FindCompletionItemForAbsolutePath(const std::string &absolute_path);

Expand Down
2 changes: 1 addition & 1 deletion src/message_handler.hh
Expand Up @@ -127,7 +127,7 @@ struct CompletionItem {
std::vector<std::string> parameters_;
int score_;
unsigned priority_;
bool use_angle_brackets_ = false;
int quote_kind_ = 0;
};

// formatting
Expand Down
15 changes: 8 additions & 7 deletions src/messages/textDocument_completion.cc
Expand Up @@ -61,9 +61,10 @@ REFLECT_STRUCT(CompletionList, isIncomplete, items);

#if LLVM_VERSION_MAJOR < 8
void DecorateIncludePaths(const std::smatch &match,
std::vector<CompletionItem> *items) {
std::vector<CompletionItem> *items,
char quote) {
std::string spaces_after_include = " ";
if (match[3].compare("include") == 0 && match[5].length())
if (match[3].compare("include") == 0 && quote != '\0')
spaces_after_include = match[4].str();

std::string prefix =
Expand All @@ -72,8 +73,7 @@ void DecorateIncludePaths(const std::smatch &match,

for (CompletionItem &item : *items) {
char quote0, quote1;
if (match[5].compare("<") == 0 ||
(match[5].length() == 0 && item.use_angle_brackets_))
if (quote != '"')
quote0 = '<', quote1 = '>';
else
quote0 = quote1 = '"';
Expand Down Expand Up @@ -504,21 +504,22 @@ void MessageHandler::textDocument_completion(CompletionParam &param,
ParseIncludeLineResult preprocess = ParseIncludeLine(buffer_line);
if (preprocess.ok && preprocess.keyword.compare("include") == 0) {
CompletionList result;
char quote = std::string(preprocess.match[5])[0];
{
std::unique_lock<std::mutex> lock(
include_complete->completion_items_mutex, std::defer_lock);
if (include_complete->is_scanning)
lock.lock();
std::string quote = preprocess.match[5];
for (auto &item : include_complete->completion_items)
if (quote.empty() || quote == (item.use_angle_brackets_ ? "<" : "\""))
if (quote == '\0' || (item.quote_kind_ & 1 && quote == '"') ||
(item.quote_kind_ & 2 && quote == '<'))
result.items.push_back(item);
}
begin_pos.character = 0;
end_pos.character = (int)buffer_line.size();
FilterCandidates(result, preprocess.pattern, begin_pos, end_pos,
buffer_line);
DecorateIncludePaths(preprocess.match, &result.items);
DecorateIncludePaths(preprocess.match, &result.items, quote);
reply(result);
return;
}
Expand Down
4 changes: 2 additions & 2 deletions src/pipeline.cc
Expand Up @@ -293,7 +293,7 @@ bool Indexer_Parse(SemaManager *completion, WorkingFiles *wfiles,
on_indexed->PushBack(std::move(update),
request.mode != IndexMode::NonInteractive);
if (entry.id >= 0) {
std::lock_guard lock2(project->mutex_);
std::shared_lock lock2(project->mtx);
project->root2folder[entry.root].path2entry_index[path] = entry.id;
}
}
Expand Down Expand Up @@ -363,7 +363,7 @@ bool Indexer_Parse(SemaManager *completion, WorkingFiles *wfiles,
vfs->state[path].loaded = true;
}
if (entry.id >= 0) {
std::lock_guard<std::mutex> lock(project->mutex_);
std::shared_lock lock(project->mtx);
auto &folder = project->root2folder[entry.root];
for (auto &dep : curr->dependencies)
folder.path2entry_index[dep.first.val().str()] = entry.id;
Expand Down

0 comments on commit ddcbc2c

Please sign in to comment.