Use Sema/CodeCompleteConsumer

This commit is contained in:
Fangrui Song 2018-07-14 10:00:04 -07:00
parent d3536831c3
commit a4dd5d0c44
2 changed files with 158 additions and 227 deletions

View File

@ -5,8 +5,10 @@
#include "log.hh" #include "log.hh"
#include "platform.h" #include "platform.h"
#include <clang/Sema/CodeCompleteConsumer.h>
#include <llvm/ADT/Twine.h> #include <llvm/ADT/Twine.h>
#include <llvm/Support/Threading.h> #include <llvm/Support/Threading.h>
using namespace clang;
using namespace llvm; using namespace llvm;
#include <algorithm> #include <algorithm>
@ -30,23 +32,16 @@ std::string StripFileType(const std::string& path) {
return Ret.str(); return Ret.str();
} }
unsigned GetCompletionPriority(const CXCompletionString& str, unsigned GetCompletionPriority(const CodeCompletionString& CCS,
CXCursorKind result_kind, CXCursorKind result_kind,
const std::optional<std::string>& typedText) { const std::optional<std::string>& typedText) {
unsigned priority = clang_getCompletionPriority(str); unsigned priority = CCS.getPriority();
if (CCS.getAvailability() != CXAvailability_Available ||
// XXX: What happens if priority overflows? result_kind == CXCursor_Destructor ||
if (result_kind == CXCursor_Destructor) { result_kind == CXCursor_ConversionFunction ||
priority *= 100;
}
if (result_kind == CXCursor_ConversionFunction ||
(result_kind == CXCursor_CXXMethod && typedText && (result_kind == CXCursor_CXXMethod && typedText &&
StartsWith(*typedText, "operator"))) { StartsWith(*typedText, "operator")))
priority *= 100; priority *= 100;
}
if (clang_getCompletionAvailability(str) != CXAvailability_Available) {
priority *= 100;
}
return priority; return priority;
} }
@ -142,85 +137,76 @@ lsCompletionItemKind GetCompletionKind(CXCursorKind cursor_kind) {
} }
} }
void BuildCompletionItemTexts(std::vector<lsCompletionItem>& out, void BuildCompletionItemTexts(std::vector<lsCompletionItem> &out,
CXCompletionString completion_string, CodeCompletionString &CCS,
bool include_snippets) { bool include_snippets) {
assert(!out.empty()); assert(!out.empty());
auto out_first = out.size() - 1; auto out_first = out.size() - 1;
std::string result_type; std::string result_type;
int num_chunks = clang_getNumCompletionChunks(completion_string); for (unsigned i = 0, num_chunks = CCS.size(); i < num_chunks; ++i) {
for (int i = 0; i < num_chunks; ++i) { const CodeCompletionString::Chunk &Chunk = CCS[i];
CXCompletionChunkKind kind = CodeCompletionString::ChunkKind Kind = Chunk.Kind;
clang_getCompletionChunkKind(completion_string, i);
std::string text; std::string text;
switch (kind) { switch (Kind) {
// clang-format off case CodeCompletionString::CK_TypedText:
case CXCompletionChunk_LeftParen: text = '('; break; case CodeCompletionString::CK_Text:
case CXCompletionChunk_RightParen: text = ')'; break; case CodeCompletionString::CK_Placeholder:
case CXCompletionChunk_LeftBracket: text = '['; break; case CodeCompletionString::CK_Informative:
case CXCompletionChunk_RightBracket: text = ']'; break; if (Chunk.Text)
case CXCompletionChunk_LeftBrace: text = '{'; break; text = Chunk.Text;
case CXCompletionChunk_RightBrace: text = '}'; break; for (auto i = out_first; i < out.size(); i++) {
case CXCompletionChunk_LeftAngle: text = '<'; break; // first TypedText is used for filtering
case CXCompletionChunk_RightAngle: text = '>'; break; if (Kind == CodeCompletionString::CK_TypedText && !out[i].filterText)
case CXCompletionChunk_Comma: text = ", "; break;
case CXCompletionChunk_Colon: text = ':'; break;
case CXCompletionChunk_SemiColon: text = ';'; break;
case CXCompletionChunk_Equal: text = '='; break;
case CXCompletionChunk_HorizontalSpace: text = ' '; break;
case CXCompletionChunk_VerticalSpace: text = ' '; break;
// clang-format on
case CXCompletionChunk_ResultType:
result_type =
ToString(clang_getCompletionChunkText(completion_string, i));
continue;
case CXCompletionChunk_TypedText:
case CXCompletionChunk_Placeholder:
case CXCompletionChunk_Text:
case CXCompletionChunk_Informative:
text = ToString(clang_getCompletionChunkText(completion_string, i));
for (auto i = out_first; i < out.size(); ++i) {
// first typed text is used for filtering
if (kind == CXCompletionChunk_TypedText && !out[i].filterText)
out[i].filterText = text; out[i].filterText = text;
if (Kind == CodeCompletionString::CK_Placeholder)
if (kind == CXCompletionChunk_Placeholder)
out[i].parameters_.push_back(text); out[i].parameters_.push_back(text);
} }
break; break;
case CodeCompletionString::CK_ResultType:
case CXCompletionChunk_CurrentParameter: if (Chunk.Text)
result_type = Chunk.Text;
continue;
case CodeCompletionString::CK_CurrentParameter:
// We have our own parsing logic for active parameter. This doesn't seem // We have our own parsing logic for active parameter. This doesn't seem
// to be very reliable. // to be very reliable.
continue; continue;
case CodeCompletionString::CK_Optional: {
case CXCompletionChunk_Optional: {
CXCompletionString nested =
clang_getCompletionChunkCompletionString(completion_string, i);
// duplicate last element, the recursive call will complete it // duplicate last element, the recursive call will complete it
out.push_back(out.back()); out.push_back(out.back());
BuildCompletionItemTexts(out, nested, include_snippets); BuildCompletionItemTexts(out, *Chunk.Optional, include_snippets);
continue; continue;
} }
// clang-format off
case CodeCompletionString::CK_LeftParen: text = '('; break;
case CodeCompletionString::CK_RightParen: text = ')'; break;
case CodeCompletionString::CK_LeftBracket: text = '['; break;
case CodeCompletionString::CK_RightBracket: text = ']'; break;
case CodeCompletionString::CK_LeftBrace: text = '{'; break;
case CodeCompletionString::CK_RightBrace: text = '}'; break;
case CodeCompletionString::CK_LeftAngle: text = '<'; break;
case CodeCompletionString::CK_RightAngle: text = '>'; break;
case CodeCompletionString::CK_Comma: text = ", "; break;
case CodeCompletionString::CK_Colon: text = ':'; break;
case CodeCompletionString::CK_SemiColon: text = ';'; break;
case CodeCompletionString::CK_Equal: text = '='; break;
case CodeCompletionString::CK_HorizontalSpace: text = ' '; break;
case CodeCompletionString::CK_VerticalSpace: text = ' '; break;
// clang-format on
} }
for (auto i = out_first; i < out.size(); ++i) for (auto i = out_first; i < out.size(); ++i)
out[i].label += text; out[i].label += text;
if (kind == CXCompletionChunk_Informative) if (Kind == CodeCompletionString::CK_Informative)
continue; continue;
for (auto i = out_first; i < out.size(); ++i) { for (auto i = out_first; i < out.size(); ++i) {
if (!include_snippets && !out[i].parameters_.empty()) if (!include_snippets && !out[i].parameters_.empty())
continue; continue;
if (kind == CXCompletionChunk_Placeholder) { if (Kind == CodeCompletionString::CK_Placeholder) {
out[i].insertText += out[i].insertText +=
"${" + std::to_string(out[i].parameters_.size()) + ":" + text + "}"; "${" + std::to_string(out[i].parameters_.size()) + ":" + text + "}";
out[i].insertTextFormat = lsInsertTextFormat::Snippet; out[i].insertTextFormat = lsInsertTextFormat::Snippet;
@ -243,106 +229,71 @@ void BuildCompletionItemTexts(std::vector<lsCompletionItem>& out,
// |do_insert|: if |!do_insert|, do not append strings to |insert| after // |do_insert|: if |!do_insert|, do not append strings to |insert| after
// a placeholder. // a placeholder.
void BuildDetailString(CXCompletionString completion_string, void BuildDetailString(const CodeCompletionString &CCS, lsCompletionItem &item,
std::string& label, bool &do_insert, std::vector<std::string> *parameters,
std::string& detail, bool include_snippets, int &angle_stack) {
std::string& insert, for (unsigned i = 0, num_chunks = CCS.size(); i < num_chunks; ++i) {
bool& do_insert, const CodeCompletionString::Chunk &Chunk = CCS[i];
lsInsertTextFormat& format, CodeCompletionString::ChunkKind Kind = Chunk.Kind;
std::vector<std::string>* parameters, const char* text = nullptr;
bool include_snippets, switch (Kind) {
int& angle_stack) { case CodeCompletionString::CK_TypedText:
int num_chunks = clang_getNumCompletionChunks(completion_string); item.label = Chunk.Text;
auto append = [&](const char* text) { [[fallthrough]];
detail += text; case CodeCompletionString::CK_Text:
if (do_insert && include_snippets) item.detail += Chunk.Text;
insert += text; if (do_insert)
}; item.insertText += Chunk.Text;
for (int i = 0; i < num_chunks; ++i) { break;
CXCompletionChunkKind kind = case CodeCompletionString::CK_Placeholder: {
clang_getCompletionChunkKind(completion_string, i); parameters->push_back(Chunk.Text);
item.detail += Chunk.Text;
switch (kind) { // Add parameter declarations as snippets if enabled
case CXCompletionChunk_Optional: { if (include_snippets) {
CXCompletionString nested = item.insertText += "${" + std::to_string(parameters->size()) + ":" + Chunk.Text + "}";
clang_getCompletionChunkCompletionString(completion_string, i); item.insertTextFormat = lsInsertTextFormat::Snippet;
// Do not add text to insert string if we're in angle brackets. } else
bool should_insert = do_insert && angle_stack == 0; do_insert = false;
BuildDetailString(nested, label, detail, insert, break;
should_insert, format, parameters, }
include_snippets, angle_stack); case CodeCompletionString::CK_Informative:
break; item.detail += Chunk.Text;
} break;
case CodeCompletionString::CK_Optional: {
case CXCompletionChunk_Placeholder: { // Do not add text to insert string if we're in angle brackets.
std::string text = bool should_insert = do_insert && angle_stack == 0;
ToString(clang_getCompletionChunkText(completion_string, i)); BuildDetailString(*Chunk.Optional, item, should_insert,
parameters->push_back(text); parameters, include_snippets, angle_stack);
detail += text; break;
// Add parameter declarations as snippets if enabled }
if (include_snippets) { case CodeCompletionString::CK_ResultType:
insert += item.detail = Chunk.Text + item.detail + " ";
"${" + std::to_string(parameters->size()) + ":" + text + "}"; break;
format = lsInsertTextFormat::Snippet; case CodeCompletionString::CK_CurrentParameter:
} else // We have our own parsing logic for active parameter. This doesn't seem
do_insert = false; // to be very reliable.
break; break;
} // clang-format off
case CodeCompletionString::CK_LeftParen: text = "("; break;
case CXCompletionChunk_CurrentParameter: case CodeCompletionString::CK_RightParen: text = ")"; break;
// We have our own parsing logic for active parameter. This doesn't seem case CodeCompletionString::CK_LeftBracket: text = "["; break;
// to be very reliable. case CodeCompletionString::CK_RightBracket: text = "]"; break;
break; case CodeCompletionString::CK_LeftBrace: text = "{"; break;
case CodeCompletionString::CK_RightBrace: text = "}"; break;
case CXCompletionChunk_TypedText: { case CodeCompletionString::CK_LeftAngle: text = "<"; angle_stack++; break;
std::string text = case CodeCompletionString::CK_RightAngle: text = ">"; angle_stack--; break;
ToString(clang_getCompletionChunkText(completion_string, i)); case CodeCompletionString::CK_Comma: text = ", "; break;
label = text; case CodeCompletionString::CK_Colon: text = ":"; break;
detail += text; case CodeCompletionString::CK_SemiColon: text = ";"; break;
if (do_insert) case CodeCompletionString::CK_Equal: text = "="; break;
insert += text; case CodeCompletionString::CK_HorizontalSpace:
break; case CodeCompletionString::CK_VerticalSpace: text = " "; break;
} // clang-format on
}
case CXCompletionChunk_Text: { if (text) {
std::string text = item.detail += text;
ToString(clang_getCompletionChunkText(completion_string, i)); if (do_insert && include_snippets)
detail += text; item.insertText += text;
if (do_insert)
insert += text;
break;
}
case CXCompletionChunk_Informative: {
detail += ToString(clang_getCompletionChunkText(completion_string, i));
break;
}
case CXCompletionChunk_ResultType: {
CXString text = clang_getCompletionChunkText(completion_string, i);
std::string new_detail = ToString(text) + detail + " ";
detail = new_detail;
break;
}
// clang-format off
case CXCompletionChunk_LeftParen: append("("); break;
case CXCompletionChunk_RightParen: append(")"); break;
case CXCompletionChunk_LeftBracket: append("["); break;
case CXCompletionChunk_RightBracket: append("]"); break;
case CXCompletionChunk_LeftBrace: append("{"); break;
case CXCompletionChunk_RightBrace: append("}"); break;
case CXCompletionChunk_LeftAngle: append("<"); angle_stack++; break;
case CXCompletionChunk_RightAngle: append(">"); angle_stack--; break;
case CXCompletionChunk_Comma: append(", "); break;
case CXCompletionChunk_Colon: append(":"); break;
case CXCompletionChunk_SemiColon: append(";"); break;
case CXCompletionChunk_Equal: append("="); break;
// clang-format on
case CXCompletionChunk_HorizontalSpace:
case CXCompletionChunk_VerticalSpace:
append(" ");
break;
} }
} }
} }
@ -483,35 +434,24 @@ void CompletionQueryMain(ClangCompleteManager* completion_manager) {
for (unsigned i = 0; i < cx_results->NumResults; ++i) { for (unsigned i = 0; i < cx_results->NumResults; ++i) {
CXCompletionResult& result = cx_results->Results[i]; CXCompletionResult& result = cx_results->Results[i];
auto CCS = (CodeCompletionString *)result.CompletionString;
// TODO: Try to figure out how we can hide base method calls without if (CCS->getAvailability() == CXAvailability_NotAvailable)
// also hiding method implementation assistance, ie,
//
// void Foo::* {
// }
//
if (clang_getCompletionAvailability(result.CompletionString) ==
CXAvailability_NotAvailable)
continue; continue;
// TODO: fill in more data lsCompletionItem ls_item;
lsCompletionItem ls_completion_item; ls_item.kind = GetCompletionKind(result.CursorKind);
if (const char* brief = CCS->getBriefComment())
ls_completion_item.kind = GetCompletionKind(result.CursorKind); ls_item.documentation = brief;
ls_completion_item.documentation =
ToString(clang_getCompletionBriefComment(result.CompletionString));
// label/detail/filterText/insertText/priority // label/detail/filterText/insertText/priority
if (g_config->completion.detailedLabel) { if (g_config->completion.detailedLabel) {
ls_completion_item.detail = ToString( ls_item.detail = CCS->getParentContextName().str();
clang_getCompletionParent(result.CompletionString, nullptr));
auto first_idx = ls_result.size(); auto first_idx = ls_result.size();
ls_result.push_back(ls_completion_item); ls_result.push_back(ls_item);
// label/filterText/insertText // label/filterText/insertText
BuildCompletionItemTexts(ls_result, result.CompletionString, BuildCompletionItemTexts(ls_result, *CCS,
g_config->client.snippetSupport); g_config->client.snippetSupport);
for (auto i = first_idx; i < ls_result.size(); ++i) { for (auto i = first_idx; i < ls_result.size(); ++i) {
@ -520,28 +460,21 @@ void CompletionQueryMain(ClangCompleteManager* completion_manager) {
ls_result[i].insertText += "$0"; ls_result[i].insertText += "$0";
} }
ls_result[i].priority_ = ls_result[i].priority_ = GetCompletionPriority(
GetCompletionPriority(result.CompletionString, result.CursorKind, *CCS, result.CursorKind, ls_result[i].filterText);
ls_result[i].filterText);
} }
} else { } else {
bool do_insert = true; bool do_insert = true;
int angle_stack = 0; int angle_stack = 0;
BuildDetailString(result.CompletionString, ls_completion_item.label, BuildDetailString(*CCS, ls_item, do_insert,
ls_completion_item.detail, &ls_item.parameters_,
ls_completion_item.insertText, do_insert,
ls_completion_item.insertTextFormat,
&ls_completion_item.parameters_,
g_config->client.snippetSupport, angle_stack); g_config->client.snippetSupport, angle_stack);
if (g_config->client.snippetSupport && if (g_config->client.snippetSupport &&
ls_completion_item.insertTextFormat == ls_item.insertTextFormat == lsInsertTextFormat::Snippet)
lsInsertTextFormat::Snippet) { ls_item.insertText += "$0";
ls_completion_item.insertText += "$0"; ls_item.priority_ =
} GetCompletionPriority(*CCS, result.CursorKind, ls_item.label);
ls_completion_item.priority_ = ls_result.push_back(ls_item);
GetCompletionPriority(result.CompletionString, result.CursorKind,
ls_completion_item.label);
ls_result.push_back(ls_completion_item);
} }
} }

View File

@ -19,10 +19,11 @@ enum class lsCompletionTriggerKind {
// Completion was triggered by typing an identifier (24x7 code // Completion was triggered by typing an identifier (24x7 code
// complete), manual invocation (e.g Ctrl+Space) or via API. // complete), manual invocation (e.g Ctrl+Space) or via API.
Invoked = 1, Invoked = 1,
// Completion was triggered by a trigger character specified by // Completion was triggered by a trigger character specified by
// the `triggerCharacters` properties of the `CompletionRegistrationOptions`. // the `triggerCharacters` properties of the `CompletionRegistrationOptions`.
TriggerCharacter = 2 TriggerCharacter = 2,
// Completion was re-triggered as the current completion list is incomplete.
TriggerForIncompleteCompletions = 3,
}; };
MAKE_REFLECT_TYPE_PROXY(lsCompletionTriggerKind); MAKE_REFLECT_TYPE_PROXY(lsCompletionTriggerKind);
@ -30,7 +31,7 @@ MAKE_REFLECT_TYPE_PROXY(lsCompletionTriggerKind);
// request is triggered. // request is triggered.
struct lsCompletionContext { struct lsCompletionContext {
// How the completion was triggered. // How the completion was triggered.
lsCompletionTriggerKind triggerKind; lsCompletionTriggerKind triggerKind = lsCompletionTriggerKind::Invoked;
// The trigger character (a single character) that has trigger code complete. // The trigger character (a single character) that has trigger code complete.
// Is undefined if `triggerKind !== CompletionTriggerKind.TriggerCharacter` // Is undefined if `triggerKind !== CompletionTriggerKind.TriggerCharacter`
@ -42,7 +43,7 @@ struct lsCompletionParams : lsTextDocumentPositionParams {
// The completion context. This is only available it the client specifies to // The completion context. This is only available it the client specifies to
// send this using // send this using
// `ClientCapabilities.textDocument.completion.contextSupport === true` // `ClientCapabilities.textDocument.completion.contextSupport === true`
std::optional<lsCompletionContext> context; lsCompletionContext context;
}; };
MAKE_REFLECT_STRUCT(lsCompletionParams, textDocument, position, context); MAKE_REFLECT_STRUCT(lsCompletionParams, textDocument, position, context);
@ -254,6 +255,7 @@ struct Handler_TextDocumentCompletion : MessageHandler {
void Run(std::unique_ptr<InMessage> message) override { void Run(std::unique_ptr<InMessage> message) override {
auto request = std::shared_ptr<In_TextDocumentComplete>( auto request = std::shared_ptr<In_TextDocumentComplete>(
static_cast<In_TextDocumentComplete*>(message.release())); static_cast<In_TextDocumentComplete*>(message.release()));
auto& params = request->params;
auto write_empty_result = [request]() { auto write_empty_result = [request]() {
Out_TextDocumentComplete out; Out_TextDocumentComplete out;
@ -261,7 +263,7 @@ struct Handler_TextDocumentCompletion : MessageHandler {
pipeline::WriteStdout(kMethodType, out); pipeline::WriteStdout(kMethodType, out);
}; };
std::string path = request->params.textDocument.uri.GetPath(); std::string path = params.textDocument.uri.GetPath();
WorkingFile* file = working_files->GetFileByFilename(path); WorkingFile* file = working_files->GetFileByFilename(path);
if (!file) { if (!file) {
write_empty_result(); write_empty_result();
@ -271,21 +273,19 @@ struct Handler_TextDocumentCompletion : MessageHandler {
// It shouldn't be possible, but sometimes vscode will send queries out // It shouldn't be possible, but sometimes vscode will send queries out
// of order, ie, we get completion request before buffer content update. // of order, ie, we get completion request before buffer content update.
std::string buffer_line; std::string buffer_line;
if (request->params.position.line >= 0 && if (params.position.line >= 0 &&
request->params.position.line < file->buffer_lines.size()) { params.position.line < file->buffer_lines.size())
buffer_line = file->buffer_lines[request->params.position.line]; buffer_line = file->buffer_lines[params.position.line];
}
// Check for - and : before completing -> or ::, since vscode does not // Check for - and : before completing -> or ::, since vscode does not
// support multi-character trigger characters. // support multi-character trigger characters.
if (request->params.context && if (params.context.triggerKind ==
request->params.context->triggerKind ==
lsCompletionTriggerKind::TriggerCharacter && lsCompletionTriggerKind::TriggerCharacter &&
request->params.context->triggerCharacter) { params.context.triggerCharacter) {
bool did_fail_check = false; bool did_fail_check = false;
std::string character = *request->params.context->triggerCharacter; std::string character = *params.context.triggerCharacter;
int preceding_index = request->params.position.character - 2; int preceding_index = params.position.character - 2;
// If the character is '"', '<' or '/', make sure that the line starts // If the character is '"', '<' or '/', make sure that the line starts
// with '#'. // with '#'.
@ -318,11 +318,11 @@ struct Handler_TextDocumentCompletion : MessageHandler {
bool is_global_completion = false; bool is_global_completion = false;
std::string existing_completion; std::string existing_completion;
lsPosition end_pos = request->params.position; lsPosition end_pos = params.position;
if (file) { if (file) {
request->params.position = file->FindStableCompletionSource( params.position = file->FindStableCompletionSource(
request->params.position, &is_global_completion, request->params.position, &is_global_completion, &existing_completion,
&existing_completion, &end_pos); &end_pos);
} }
ParseIncludeLineResult result = ParseIncludeLine(buffer_line); ParseIncludeLineResult result = ParseIncludeLine(buffer_line);
@ -359,16 +359,16 @@ struct Handler_TextDocumentCompletion : MessageHandler {
} }
for (lsCompletionItem& item : out.result.items) { for (lsCompletionItem& item : out.result.items) {
item.textEdit->range.start.line = request->params.position.line; item.textEdit->range.start.line = params.position.line;
item.textEdit->range.start.character = 0; item.textEdit->range.start.character = 0;
item.textEdit->range.end.line = request->params.position.line; item.textEdit->range.end.line = params.position.line;
item.textEdit->range.end.character = (int)buffer_line.size(); item.textEdit->range.end.character = (int)buffer_line.size();
} }
pipeline::WriteStdout(kMethodType, out); pipeline::WriteStdout(kMethodType, out);
} else { } else {
ClangCompleteManager::OnComplete callback = std::bind( ClangCompleteManager::OnComplete callback = std::bind(
[this, request, is_global_completion, existing_completion, [this, request, params, is_global_completion, existing_completion,
has_open_paren](const std::vector<lsCompletionItem>& results, has_open_paren](const std::vector<lsCompletionItem>& results,
bool is_cached_result) { bool is_cached_result) {
Out_TextDocumentComplete out; Out_TextDocumentComplete out;
@ -381,7 +381,7 @@ struct Handler_TextDocumentCompletion : MessageHandler {
// Cache completion results. // Cache completion results.
if (!is_cached_result) { if (!is_cached_result) {
std::string path = request->params.textDocument.uri.GetPath(); std::string path = params.textDocument.uri.GetPath();
if (is_global_completion) { if (is_global_completion) {
global_code_complete_cache->WithLock([&]() { global_code_complete_cache->WithLock([&]() {
global_code_complete_cache->cached_path_ = path; global_code_complete_cache->cached_path_ = path;
@ -391,7 +391,7 @@ struct Handler_TextDocumentCompletion : MessageHandler {
non_global_code_complete_cache->WithLock([&]() { non_global_code_complete_cache->WithLock([&]() {
non_global_code_complete_cache->cached_path_ = path; non_global_code_complete_cache->cached_path_ = path;
non_global_code_complete_cache->cached_completion_position_ = non_global_code_complete_cache->cached_completion_position_ =
request->params.position; params.position;
non_global_code_complete_cache->cached_results_ = results; non_global_code_complete_cache->cached_results_ = results;
}); });
} }
@ -421,16 +421,14 @@ struct Handler_TextDocumentCompletion : MessageHandler {
callback(global_code_complete_cache->cached_results_, callback(global_code_complete_cache->cached_results_,
true /*is_cached_result*/); true /*is_cached_result*/);
}); });
clang_complete->CodeComplete(request->id, request->params, clang_complete->CodeComplete(request->id, params, freshen_global);
freshen_global); } else if (non_global_code_complete_cache->IsCacheValid(params)) {
} else if (non_global_code_complete_cache->IsCacheValid(
request->params)) {
non_global_code_complete_cache->WithLock([&]() { non_global_code_complete_cache->WithLock([&]() {
callback(non_global_code_complete_cache->cached_results_, callback(non_global_code_complete_cache->cached_results_,
true /*is_cached_result*/); true /*is_cached_result*/);
}); });
} else { } else {
clang_complete->CodeComplete(request->id, request->params, callback); clang_complete->CodeComplete(request->id, params, callback);
} }
} }
} }