2017-05-27 04:21:00 +00:00
|
|
|
#include "clang_complete.h"
|
2017-03-26 21:40:34 +00:00
|
|
|
|
2017-05-20 06:35:14 +00:00
|
|
|
#include "clang_utils.h"
|
2018-05-27 19:24:56 +00:00
|
|
|
#include "filesystem.hh"
|
|
|
|
#include "log.hh"
|
2017-05-10 04:00:05 +00:00
|
|
|
#include "platform.h"
|
2017-03-26 21:40:34 +00:00
|
|
|
#include "timer.h"
|
|
|
|
|
2018-05-27 19:24:56 +00:00
|
|
|
#include <llvm/ADT/Twine.h>
|
|
|
|
#include <llvm/Support/Threading.h>
|
2018-05-13 20:30:24 +00:00
|
|
|
using namespace llvm;
|
|
|
|
|
2017-03-26 21:40:34 +00:00
|
|
|
#include <algorithm>
|
2017-04-17 01:22:59 +00:00
|
|
|
#include <thread>
|
2017-03-26 21:40:34 +00:00
|
|
|
|
|
|
|
namespace {
|
2017-06-29 02:50:30 +00:00
|
|
|
|
2017-03-26 21:40:34 +00:00
|
|
|
unsigned Flags() {
|
2017-05-25 02:04:19 +00:00
|
|
|
// TODO: use clang_defaultEditingTranslationUnitOptions()?
|
2017-09-22 01:14:57 +00:00
|
|
|
return CXTranslationUnit_Incomplete | CXTranslationUnit_KeepGoing |
|
|
|
|
CXTranslationUnit_CacheCompletionResults |
|
|
|
|
CXTranslationUnit_PrecompiledPreamble |
|
2017-09-27 06:03:43 +00:00
|
|
|
CXTranslationUnit_IncludeBriefCommentsInCodeCompletion |
|
|
|
|
CXTranslationUnit_DetailedPreprocessingRecord
|
2017-05-09 05:15:35 +00:00
|
|
|
#if !defined(_WIN32)
|
2017-09-22 01:14:57 +00:00
|
|
|
// For whatever reason, CreatePreambleOnFirstParse causes clang to
|
|
|
|
// become very crashy on windows.
|
|
|
|
// TODO: do more investigation, submit fixes to clang.
|
|
|
|
| CXTranslationUnit_CreatePreambleOnFirstParse
|
2017-05-09 05:15:35 +00:00
|
|
|
#endif
|
2017-09-22 01:14:57 +00:00
|
|
|
;
|
2017-05-10 04:52:15 +00:00
|
|
|
}
|
|
|
|
|
2018-04-08 00:10:54 +00:00
|
|
|
std::string StripFileType(const std::string& path) {
|
2018-05-13 20:30:24 +00:00
|
|
|
SmallString<128> Ret;
|
|
|
|
sys::path::append(Ret, sys::path::parent_path(path), sys::path::stem(path));
|
|
|
|
return Ret.str();
|
2018-04-08 00:10:54 +00:00
|
|
|
}
|
|
|
|
|
2017-12-06 17:46:24 +00:00
|
|
|
unsigned GetCompletionPriority(const CXCompletionString& str,
|
2017-12-07 19:53:48 +00:00
|
|
|
CXCursorKind result_kind,
|
2018-03-31 03:16:33 +00:00
|
|
|
const std::optional<std::string>& typedText) {
|
2017-12-06 17:46:24 +00:00
|
|
|
unsigned priority = clang_getCompletionPriority(str);
|
|
|
|
|
|
|
|
// XXX: What happens if priority overflows?
|
2017-05-10 04:52:15 +00:00
|
|
|
if (result_kind == CXCursor_Destructor) {
|
|
|
|
priority *= 100;
|
|
|
|
}
|
|
|
|
if (result_kind == CXCursor_ConversionFunction ||
|
2018-02-07 13:01:42 +00:00
|
|
|
(result_kind == CXCursor_CXXMethod && typedText &&
|
|
|
|
StartsWith(*typedText, "operator"))) {
|
2017-05-10 04:52:15 +00:00
|
|
|
priority *= 100;
|
|
|
|
}
|
|
|
|
if (clang_getCompletionAvailability(str) != CXAvailability_Available) {
|
|
|
|
priority *= 100;
|
|
|
|
}
|
|
|
|
return priority;
|
2017-03-26 21:40:34 +00:00
|
|
|
}
|
|
|
|
|
2017-06-29 17:30:22 +00:00
|
|
|
/*
|
2017-05-15 07:28:53 +00:00
|
|
|
bool IsCallKind(CXCursorKind kind) {
|
|
|
|
switch (kind) {
|
|
|
|
case CXCursor_ObjCInstanceMethodDecl:
|
|
|
|
case CXCursor_CXXMethod:
|
|
|
|
case CXCursor_FunctionTemplate:
|
|
|
|
case CXCursor_FunctionDecl:
|
|
|
|
case CXCursor_Constructor:
|
|
|
|
case CXCursor_Destructor:
|
|
|
|
case CXCursor_ConversionFunction:
|
|
|
|
return true;
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
2017-06-29 17:30:22 +00:00
|
|
|
*/
|
2017-05-15 07:28:53 +00:00
|
|
|
|
2017-03-26 21:40:34 +00:00
|
|
|
lsCompletionItemKind GetCompletionKind(CXCursorKind cursor_kind) {
|
|
|
|
switch (cursor_kind) {
|
2017-09-22 02:45:17 +00:00
|
|
|
case CXCursor_UnexposedDecl:
|
|
|
|
return lsCompletionItemKind::Text;
|
|
|
|
|
2018-03-01 05:49:44 +00:00
|
|
|
case CXCursor_StructDecl:
|
|
|
|
case CXCursor_UnionDecl:
|
|
|
|
return lsCompletionItemKind::Struct;
|
|
|
|
case CXCursor_ClassDecl:
|
|
|
|
return lsCompletionItemKind::Class;
|
|
|
|
case CXCursor_EnumDecl:
|
|
|
|
return lsCompletionItemKind::Enum;
|
|
|
|
case CXCursor_FieldDecl:
|
|
|
|
return lsCompletionItemKind::Field;
|
|
|
|
case CXCursor_EnumConstantDecl:
|
|
|
|
return lsCompletionItemKind::EnumMember;
|
|
|
|
case CXCursor_FunctionDecl:
|
|
|
|
return lsCompletionItemKind::Function;
|
|
|
|
case CXCursor_VarDecl:
|
|
|
|
case CXCursor_ParmDecl:
|
|
|
|
return lsCompletionItemKind::Variable;
|
|
|
|
case CXCursor_ObjCInterfaceDecl:
|
|
|
|
return lsCompletionItemKind::Interface;
|
|
|
|
|
2017-05-15 07:28:53 +00:00
|
|
|
case CXCursor_ObjCInstanceMethodDecl:
|
|
|
|
case CXCursor_CXXMethod:
|
2017-12-15 05:18:43 +00:00
|
|
|
case CXCursor_ObjCClassMethodDecl:
|
2017-05-15 07:28:53 +00:00
|
|
|
return lsCompletionItemKind::Method;
|
|
|
|
|
|
|
|
case CXCursor_FunctionTemplate:
|
2018-03-20 02:51:42 +00:00
|
|
|
return lsCompletionItemKind::Function;
|
2017-05-15 07:28:53 +00:00
|
|
|
|
|
|
|
case CXCursor_Constructor:
|
|
|
|
case CXCursor_Destructor:
|
|
|
|
case CXCursor_ConversionFunction:
|
|
|
|
return lsCompletionItemKind::Constructor;
|
|
|
|
|
2017-12-15 05:18:43 +00:00
|
|
|
case CXCursor_ObjCIvarDecl:
|
2017-05-15 07:28:53 +00:00
|
|
|
return lsCompletionItemKind::Variable;
|
|
|
|
|
|
|
|
case CXCursor_ClassTemplate:
|
|
|
|
case CXCursor_ClassTemplatePartialSpecialization:
|
|
|
|
case CXCursor_UsingDeclaration:
|
|
|
|
case CXCursor_TypedefDecl:
|
|
|
|
case CXCursor_TypeAliasDecl:
|
|
|
|
case CXCursor_TypeAliasTemplateDecl:
|
2017-12-15 05:18:43 +00:00
|
|
|
case CXCursor_ObjCCategoryDecl:
|
|
|
|
case CXCursor_ObjCProtocolDecl:
|
|
|
|
case CXCursor_ObjCImplementationDecl:
|
|
|
|
case CXCursor_ObjCCategoryImplDecl:
|
2017-05-15 07:28:53 +00:00
|
|
|
return lsCompletionItemKind::Class;
|
|
|
|
|
2018-03-01 05:49:44 +00:00
|
|
|
case CXCursor_ObjCPropertyDecl:
|
|
|
|
return lsCompletionItemKind::Property;
|
2017-05-15 07:28:53 +00:00
|
|
|
|
|
|
|
case CXCursor_MacroInstantiation:
|
|
|
|
case CXCursor_MacroDefinition:
|
|
|
|
return lsCompletionItemKind::Interface;
|
|
|
|
|
|
|
|
case CXCursor_Namespace:
|
|
|
|
case CXCursor_NamespaceAlias:
|
|
|
|
case CXCursor_NamespaceRef:
|
|
|
|
return lsCompletionItemKind::Module;
|
|
|
|
|
|
|
|
case CXCursor_MemberRef:
|
|
|
|
case CXCursor_TypeRef:
|
2017-12-15 05:18:43 +00:00
|
|
|
case CXCursor_ObjCSuperClassRef:
|
|
|
|
case CXCursor_ObjCProtocolRef:
|
|
|
|
case CXCursor_ObjCClassRef:
|
2017-05-15 07:28:53 +00:00
|
|
|
return lsCompletionItemKind::Reference;
|
|
|
|
|
2018-01-11 02:43:01 +00:00
|
|
|
// return lsCompletionItemKind::Unit;
|
|
|
|
// return lsCompletionItemKind::Value;
|
|
|
|
// return lsCompletionItemKind::Keyword;
|
|
|
|
// return lsCompletionItemKind::Snippet;
|
|
|
|
// return lsCompletionItemKind::Color;
|
|
|
|
// return lsCompletionItemKind::File;
|
2017-05-15 07:28:53 +00:00
|
|
|
|
|
|
|
case CXCursor_NotImplemented:
|
2018-01-20 18:06:19 +00:00
|
|
|
case CXCursor_OverloadCandidate:
|
2017-05-15 07:28:53 +00:00
|
|
|
return lsCompletionItemKind::Text;
|
|
|
|
|
2018-03-01 05:49:44 +00:00
|
|
|
case CXCursor_TemplateTypeParameter:
|
|
|
|
case CXCursor_TemplateTemplateParameter:
|
|
|
|
return lsCompletionItemKind::TypeParameter;
|
2017-12-30 00:32:43 +00:00
|
|
|
|
2017-05-15 07:28:53 +00:00
|
|
|
default:
|
2017-09-22 02:45:17 +00:00
|
|
|
LOG_S(WARNING) << "Unhandled completion kind " << cursor_kind;
|
2017-05-15 07:28:53 +00:00
|
|
|
return lsCompletionItemKind::Text;
|
2017-03-26 21:40:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
add detailedLabel completion style
Some completion UI, such as Emacs' completion-at-point and company-lsp,
display completion item label and detail side by side.
This does not look right, when you see things like:
"foo" "int foo()"
"bar" "void bar(int i = 0)"
When this option is enabled, the completion item label is very detailed,
it shows the full signature of the candidate.
The detail just contains the completion item parent context.
Also, in this mode, functions with default arguments,
generates one more item per default argument
so that the right function call can be selected.
That is, you get something like:
"int foo()" "Foo"
"void bar()" "Foo"
"void bar(int i = 0)" "Foo"
Be wary, this is quickly quite verbose,
items can end up truncated by the UIs.
2018-02-03 17:32:55 +00:00
|
|
|
void BuildCompletionItemTexts(std::vector<lsCompletionItem>& out,
|
|
|
|
CXCompletionString completion_string,
|
|
|
|
bool include_snippets) {
|
|
|
|
assert(!out.empty());
|
|
|
|
auto out_first = out.size() - 1;
|
|
|
|
|
|
|
|
std::string result_type;
|
|
|
|
|
|
|
|
int num_chunks = clang_getNumCompletionChunks(completion_string);
|
|
|
|
for (int i = 0; i < num_chunks; ++i) {
|
|
|
|
CXCompletionChunkKind kind =
|
2018-02-22 07:34:32 +00:00
|
|
|
clang_getCompletionChunkKind(completion_string, i);
|
add detailedLabel completion style
Some completion UI, such as Emacs' completion-at-point and company-lsp,
display completion item label and detail side by side.
This does not look right, when you see things like:
"foo" "int foo()"
"bar" "void bar(int i = 0)"
When this option is enabled, the completion item label is very detailed,
it shows the full signature of the candidate.
The detail just contains the completion item parent context.
Also, in this mode, functions with default arguments,
generates one more item per default argument
so that the right function call can be selected.
That is, you get something like:
"int foo()" "Foo"
"void bar()" "Foo"
"void bar(int i = 0)" "Foo"
Be wary, this is quickly quite verbose,
items can end up truncated by the UIs.
2018-02-03 17:32:55 +00:00
|
|
|
|
|
|
|
std::string text;
|
|
|
|
switch (kind) {
|
2018-03-01 05:49:44 +00:00
|
|
|
// clang-format off
|
|
|
|
case CXCompletionChunk_LeftParen: text = '('; break;
|
|
|
|
case CXCompletionChunk_RightParen: text = ')'; break;
|
|
|
|
case CXCompletionChunk_LeftBracket: text = '['; break;
|
|
|
|
case CXCompletionChunk_RightBracket: text = ']'; break;
|
|
|
|
case CXCompletionChunk_LeftBrace: text = '{'; break;
|
|
|
|
case CXCompletionChunk_RightBrace: text = '}'; break;
|
|
|
|
case CXCompletionChunk_LeftAngle: text = '<'; break;
|
|
|
|
case CXCompletionChunk_RightAngle: text = '>'; break;
|
|
|
|
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;
|
2018-03-20 02:51:42 +00:00
|
|
|
// clang-format on
|
2018-02-22 07:34:32 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
if (kind == CXCompletionChunk_Placeholder)
|
|
|
|
out[i].parameters_.push_back(text);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CXCompletionChunk_CurrentParameter:
|
|
|
|
// We have our own parsing logic for active parameter. This doesn't seem
|
|
|
|
// to be very reliable.
|
|
|
|
continue;
|
|
|
|
|
|
|
|
case CXCompletionChunk_Optional: {
|
|
|
|
CXCompletionString nested =
|
|
|
|
clang_getCompletionChunkCompletionString(completion_string, i);
|
|
|
|
// duplicate last element, the recursive call will complete it
|
|
|
|
out.push_back(out.back());
|
|
|
|
BuildCompletionItemTexts(out, nested, include_snippets);
|
|
|
|
continue;
|
add detailedLabel completion style
Some completion UI, such as Emacs' completion-at-point and company-lsp,
display completion item label and detail side by side.
This does not look right, when you see things like:
"foo" "int foo()"
"bar" "void bar(int i = 0)"
When this option is enabled, the completion item label is very detailed,
it shows the full signature of the candidate.
The detail just contains the completion item parent context.
Also, in this mode, functions with default arguments,
generates one more item per default argument
so that the right function call can be selected.
That is, you get something like:
"int foo()" "Foo"
"void bar()" "Foo"
"void bar(int i = 0)" "Foo"
Be wary, this is quickly quite verbose,
items can end up truncated by the UIs.
2018-02-03 17:32:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (auto i = out_first; i < out.size(); ++i)
|
|
|
|
out[i].label += text;
|
|
|
|
|
|
|
|
if (kind == CXCompletionChunk_Informative)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
for (auto i = out_first; i < out.size(); ++i) {
|
|
|
|
if (!include_snippets && !out[i].parameters_.empty())
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (kind == CXCompletionChunk_Placeholder) {
|
|
|
|
out[i].insertText +=
|
|
|
|
"${" + std::to_string(out[i].parameters_.size()) + ":" + text + "}";
|
|
|
|
out[i].insertTextFormat = lsInsertTextFormat::Snippet;
|
|
|
|
} else {
|
|
|
|
out[i].insertText += text;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (result_type.empty())
|
|
|
|
return;
|
|
|
|
|
|
|
|
for (auto i = out_first; i < out.size(); ++i) {
|
|
|
|
// ' : ' for variables,
|
|
|
|
// ' -> ' (trailing return type-like) for functions
|
|
|
|
out[i].label += (out[i].label == out[i].filterText ? " : " : " -> ");
|
|
|
|
out[i].label += result_type;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-23 06:21:32 +00:00
|
|
|
// |do_insert|: if |!do_insert|, do not append strings to |insert| after
|
|
|
|
// a placeholder.
|
2017-09-22 01:14:57 +00:00
|
|
|
void BuildDetailString(CXCompletionString completion_string,
|
|
|
|
std::string& label,
|
|
|
|
std::string& detail,
|
|
|
|
std::string& insert,
|
2018-01-23 06:21:32 +00:00
|
|
|
bool& do_insert,
|
2017-12-05 17:21:54 +00:00
|
|
|
lsInsertTextFormat& format,
|
2017-11-19 12:57:16 +00:00
|
|
|
std::vector<std::string>* parameters,
|
2018-04-14 16:52:17 +00:00
|
|
|
bool include_snippets,
|
|
|
|
int& angle_stack) {
|
2017-03-26 21:40:34 +00:00
|
|
|
int num_chunks = clang_getNumCompletionChunks(completion_string);
|
2018-02-01 20:21:58 +00:00
|
|
|
auto append = [&](const char* text) {
|
|
|
|
detail += text;
|
2018-05-09 05:01:58 +00:00
|
|
|
if (do_insert && include_snippets)
|
2018-02-01 20:21:58 +00:00
|
|
|
insert += text;
|
|
|
|
};
|
2017-04-16 08:55:14 +00:00
|
|
|
for (int i = 0; i < num_chunks; ++i) {
|
2017-09-22 01:14:57 +00:00
|
|
|
CXCompletionChunkKind kind =
|
|
|
|
clang_getCompletionChunkKind(completion_string, i);
|
2017-03-26 21:40:34 +00:00
|
|
|
|
|
|
|
switch (kind) {
|
2017-09-22 01:14:57 +00:00
|
|
|
case CXCompletionChunk_Optional: {
|
|
|
|
CXCompletionString nested =
|
|
|
|
clang_getCompletionChunkCompletionString(completion_string, i);
|
2018-04-14 16:52:17 +00:00
|
|
|
// Do not add text to insert string if we're in angle brackets.
|
|
|
|
bool should_insert = do_insert && angle_stack == 0;
|
|
|
|
BuildDetailString(nested, label, detail, insert,
|
|
|
|
should_insert, format, parameters,
|
|
|
|
include_snippets, angle_stack);
|
2017-09-22 01:14:57 +00:00
|
|
|
break;
|
|
|
|
}
|
2017-03-26 21:40:34 +00:00
|
|
|
|
2017-09-22 01:14:57 +00:00
|
|
|
case CXCompletionChunk_Placeholder: {
|
|
|
|
std::string text =
|
2017-11-04 22:29:03 +00:00
|
|
|
ToString(clang_getCompletionChunkText(completion_string, i));
|
2017-09-22 01:14:57 +00:00
|
|
|
parameters->push_back(text);
|
|
|
|
detail += text;
|
2017-11-19 12:57:16 +00:00
|
|
|
// Add parameter declarations as snippets if enabled
|
2017-11-19 22:11:54 +00:00
|
|
|
if (include_snippets) {
|
|
|
|
insert +=
|
|
|
|
"${" + std::to_string(parameters->size()) + ":" + text + "}";
|
2017-12-05 17:21:54 +00:00
|
|
|
format = lsInsertTextFormat::Snippet;
|
2018-01-23 06:21:32 +00:00
|
|
|
} else
|
|
|
|
do_insert = false;
|
2017-09-22 01:14:57 +00:00
|
|
|
break;
|
|
|
|
}
|
2017-05-15 07:28:53 +00:00
|
|
|
|
2017-09-22 01:14:57 +00:00
|
|
|
case CXCompletionChunk_CurrentParameter:
|
|
|
|
// We have our own parsing logic for active parameter. This doesn't seem
|
|
|
|
// to be very reliable.
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CXCompletionChunk_TypedText: {
|
|
|
|
std::string text =
|
2017-11-04 22:29:03 +00:00
|
|
|
ToString(clang_getCompletionChunkText(completion_string, i));
|
2017-09-22 01:14:57 +00:00
|
|
|
label = text;
|
|
|
|
detail += text;
|
2018-01-23 06:21:32 +00:00
|
|
|
if (do_insert)
|
|
|
|
insert += text;
|
2017-09-22 01:14:57 +00:00
|
|
|
break;
|
|
|
|
}
|
2017-03-26 21:40:34 +00:00
|
|
|
|
2017-09-22 01:14:57 +00:00
|
|
|
case CXCompletionChunk_Text: {
|
|
|
|
std::string text =
|
2017-11-04 22:29:03 +00:00
|
|
|
ToString(clang_getCompletionChunkText(completion_string, i));
|
2017-09-22 01:14:57 +00:00
|
|
|
detail += text;
|
2018-01-23 06:21:32 +00:00
|
|
|
if (do_insert)
|
|
|
|
insert += text;
|
2017-09-22 01:14:57 +00:00
|
|
|
break;
|
|
|
|
}
|
2017-05-15 07:58:29 +00:00
|
|
|
|
2017-09-22 01:14:57 +00:00
|
|
|
case CXCompletionChunk_Informative: {
|
2017-11-04 22:29:03 +00:00
|
|
|
detail += ToString(clang_getCompletionChunkText(completion_string, i));
|
2017-09-22 01:14:57 +00:00
|
|
|
break;
|
|
|
|
}
|
2017-05-15 07:58:29 +00:00
|
|
|
|
2017-09-22 01:14:57 +00:00
|
|
|
case CXCompletionChunk_ResultType: {
|
|
|
|
CXString text = clang_getCompletionChunkText(completion_string, i);
|
2017-11-04 22:29:03 +00:00
|
|
|
std::string new_detail = ToString(text) + detail + " ";
|
2017-09-22 01:14:57 +00:00
|
|
|
detail = new_detail;
|
|
|
|
break;
|
|
|
|
}
|
2017-03-26 21:40:34 +00:00
|
|
|
|
2018-02-01 20:21:58 +00:00
|
|
|
// 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;
|
2018-04-14 16:52:17 +00:00
|
|
|
case CXCompletionChunk_LeftAngle: append("<"); angle_stack++; break;
|
|
|
|
case CXCompletionChunk_RightAngle: append(">"); angle_stack--; break;
|
2018-02-01 20:21:58 +00:00
|
|
|
case CXCompletionChunk_Comma: append(", "); break;
|
|
|
|
case CXCompletionChunk_Colon: append(":"); break;
|
|
|
|
case CXCompletionChunk_SemiColon: append(";"); break;
|
|
|
|
case CXCompletionChunk_Equal: append("="); break;
|
|
|
|
// clang-format on
|
2017-09-22 01:14:57 +00:00
|
|
|
case CXCompletionChunk_HorizontalSpace:
|
|
|
|
case CXCompletionChunk_VerticalSpace:
|
2018-02-01 20:21:58 +00:00
|
|
|
append(" ");
|
2017-09-22 01:14:57 +00:00
|
|
|
break;
|
2017-03-26 21:40:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-04-17 01:22:59 +00:00
|
|
|
|
2017-10-23 05:07:50 +00:00
|
|
|
void TryEnsureDocumentParsed(ClangCompleteManager* manager,
|
|
|
|
std::shared_ptr<CompletionSession> session,
|
2017-11-11 19:41:09 +00:00
|
|
|
std::unique_ptr<ClangTranslationUnit>* tu,
|
2018-04-14 16:52:17 +00:00
|
|
|
ClangIndex* index,
|
|
|
|
bool emit_diag) {
|
2017-05-26 06:40:38 +00:00
|
|
|
// Nothing to do. We already have a translation unit.
|
|
|
|
if (*tu)
|
2017-05-11 06:25:41 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
std::vector<std::string> args = session->file.args;
|
2017-10-18 08:24:52 +00:00
|
|
|
|
|
|
|
// -fspell-checking enables FixIts for, ie, misspelled types.
|
|
|
|
if (!AnyStartsWith(args, "-fno-spell-checking") &&
|
|
|
|
!AnyStartsWith(args, "-fspell-checking")) {
|
|
|
|
args.push_back("-fspell-checking");
|
|
|
|
}
|
|
|
|
|
2018-01-30 00:27:43 +00:00
|
|
|
WorkingFiles::Snapshot snapshot = session->working_files->AsSnapshot(
|
|
|
|
{StripFileType(session->file.filename)});
|
2018-01-06 05:45:20 +00:00
|
|
|
std::vector<CXUnsavedFile> unsaved = snapshot.AsUnsavedFiles();
|
2017-05-11 06:25:41 +00:00
|
|
|
|
2017-09-22 02:45:17 +00:00
|
|
|
LOG_S(INFO) << "Creating completion session with arguments "
|
2018-02-21 07:52:49 +00:00
|
|
|
<< StringJoin(args, " ");
|
2017-11-11 19:41:09 +00:00
|
|
|
*tu = ClangTranslationUnit::Create(index, session->file.filename, args,
|
|
|
|
unsaved, Flags());
|
2017-06-10 04:13:16 +00:00
|
|
|
|
|
|
|
// Build diagnostics.
|
2018-04-04 06:05:41 +00:00
|
|
|
if (g_config->diagnostics.onParse && *tu) {
|
2017-10-23 05:07:50 +00:00
|
|
|
// If we're emitting diagnostics, do an immediate reparse, otherwise we will
|
2017-10-17 18:43:33 +00:00
|
|
|
// emit stale/bad diagnostics.
|
2017-11-11 19:41:09 +00:00
|
|
|
*tu = ClangTranslationUnit::Reparse(std::move(*tu), unsaved);
|
2017-10-23 15:38:01 +00:00
|
|
|
if (!*tu) {
|
|
|
|
LOG_S(ERROR) << "Reparsing translation unit for diagnostics failed for "
|
|
|
|
<< session->file.filename;
|
|
|
|
return;
|
|
|
|
}
|
2017-10-17 18:43:33 +00:00
|
|
|
|
2017-12-12 05:20:29 +00:00
|
|
|
std::vector<lsDiagnostic> ls_diagnostics;
|
2017-06-10 04:13:16 +00:00
|
|
|
unsigned num_diagnostics = clang_getNumDiagnostics((*tu)->cx_tu);
|
|
|
|
for (unsigned i = 0; i < num_diagnostics; ++i) {
|
2018-03-31 03:16:33 +00:00
|
|
|
std::optional<lsDiagnostic> diagnostic = BuildAndDisposeDiagnostic(
|
2017-07-12 22:02:48 +00:00
|
|
|
clang_getDiagnostic((*tu)->cx_tu, i), session->file.filename);
|
2017-12-23 23:20:13 +00:00
|
|
|
// Filter messages like "too many errors emitted, stopping now
|
|
|
|
// [-ferror-limit=]" which has line = 0 and got subtracted by 1 after
|
|
|
|
// conversion to lsDiagnostic
|
|
|
|
if (diagnostic && diagnostic->range.start.line >= 0)
|
2017-06-10 04:13:16 +00:00
|
|
|
ls_diagnostics.push_back(*diagnostic);
|
|
|
|
}
|
|
|
|
manager->on_diagnostic_(session->file.filename, ls_diagnostics);
|
|
|
|
}
|
2017-05-11 06:25:41 +00:00
|
|
|
}
|
|
|
|
|
2018-04-14 16:52:17 +00:00
|
|
|
void CompletionPreloadMain(ClangCompleteManager* completion_manager) {
|
2017-05-11 06:25:41 +00:00
|
|
|
while (true) {
|
|
|
|
// Fetching the completion request blocks until we have a request.
|
2018-04-14 16:52:17 +00:00
|
|
|
auto request = completion_manager->preload_requests_.Dequeue();
|
2017-06-10 01:02:48 +00:00
|
|
|
|
2017-05-26 06:40:38 +00:00
|
|
|
// If we don't get a session then that means we don't care about the file
|
|
|
|
// anymore - abandon the request.
|
2017-09-22 01:14:57 +00:00
|
|
|
std::shared_ptr<CompletionSession> session =
|
2017-10-23 07:28:21 +00:00
|
|
|
completion_manager->TryGetSession(request.path,
|
|
|
|
false /*mark_as_completion*/,
|
|
|
|
false /*create_if_needed*/);
|
2017-05-26 06:40:38 +00:00
|
|
|
if (!session)
|
|
|
|
continue;
|
2017-05-11 06:25:41 +00:00
|
|
|
|
2018-04-14 16:52:17 +00:00
|
|
|
// Note: we only preload completion. We emit diagnostics for the
|
|
|
|
// completion preload though.
|
|
|
|
CompletionSession::Tu* tu = &session->completion;
|
|
|
|
|
2017-05-26 06:40:38 +00:00
|
|
|
// If we've parsed it more recently than the request time, don't bother
|
|
|
|
// reparsing.
|
2018-04-14 16:52:17 +00:00
|
|
|
if (tu->last_parsed_at && *tu->last_parsed_at > request.request_time)
|
2017-05-26 06:40:38 +00:00
|
|
|
continue;
|
2017-05-11 06:25:41 +00:00
|
|
|
|
2017-11-11 19:41:09 +00:00
|
|
|
std::unique_ptr<ClangTranslationUnit> parsing;
|
2018-04-14 16:52:17 +00:00
|
|
|
TryEnsureDocumentParsed(completion_manager, session, &parsing, &tu->index,
|
|
|
|
true);
|
2017-05-26 06:40:38 +00:00
|
|
|
|
|
|
|
// Activate new translation unit.
|
2018-04-14 16:52:17 +00:00
|
|
|
std::lock_guard<std::mutex> lock(tu->lock);
|
|
|
|
tu->last_parsed_at = std::chrono::high_resolution_clock::now();
|
|
|
|
tu->tu = std::move(parsing);
|
2017-05-11 06:25:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-05-27 04:21:00 +00:00
|
|
|
void CompletionQueryMain(ClangCompleteManager* completion_manager) {
|
2017-04-17 01:22:59 +00:00
|
|
|
while (true) {
|
2017-05-10 04:52:15 +00:00
|
|
|
// Fetching the completion request blocks until we have a request.
|
2017-09-22 01:14:57 +00:00
|
|
|
std::unique_ptr<ClangCompleteManager::CompletionRequest> request =
|
2018-02-18 17:15:39 +00:00
|
|
|
completion_manager->completion_request_.Dequeue();
|
2018-02-22 07:13:42 +00:00
|
|
|
|
|
|
|
// Drop older requests if we're not buffering.
|
2018-04-04 06:05:41 +00:00
|
|
|
while (g_config->completion.dropOldRequests &&
|
2018-02-22 07:13:42 +00:00
|
|
|
!completion_manager->completion_request_.IsEmpty()) {
|
|
|
|
completion_manager->on_dropped_(request->id);
|
|
|
|
request = completion_manager->completion_request_.Dequeue();
|
|
|
|
}
|
|
|
|
|
2017-09-22 02:25:33 +00:00
|
|
|
std::string path = request->document.uri.GetPath();
|
2017-04-17 01:22:59 +00:00
|
|
|
|
2017-09-22 01:14:57 +00:00
|
|
|
std::shared_ptr<CompletionSession> session =
|
2017-10-23 07:28:21 +00:00
|
|
|
completion_manager->TryGetSession(path, true /*mark_as_completion*/,
|
|
|
|
true /*create_if_needed*/);
|
2017-06-10 01:02:48 +00:00
|
|
|
|
2018-04-14 16:52:17 +00:00
|
|
|
std::lock_guard<std::mutex> lock(session->completion.lock);
|
2017-10-17 18:43:33 +00:00
|
|
|
Timer timer;
|
2018-04-14 16:52:17 +00:00
|
|
|
TryEnsureDocumentParsed(completion_manager, session, &session->completion.tu,
|
|
|
|
&session->completion.index, false);
|
2017-10-23 05:07:50 +00:00
|
|
|
timer.ResetAndPrint("[complete] TryEnsureDocumentParsed");
|
|
|
|
|
|
|
|
// It is possible we failed to create the document despite
|
|
|
|
// |TryEnsureDocumentParsed|.
|
2018-04-14 16:52:17 +00:00
|
|
|
if (!session->completion.tu)
|
2017-10-23 05:07:50 +00:00
|
|
|
continue;
|
2017-04-17 01:22:59 +00:00
|
|
|
|
2018-01-12 17:37:33 +00:00
|
|
|
timer.Reset();
|
|
|
|
WorkingFiles::Snapshot snapshot =
|
|
|
|
completion_manager->working_files_->AsSnapshot({StripFileType(path)});
|
2018-01-06 05:45:20 +00:00
|
|
|
std::vector<CXUnsavedFile> unsaved = snapshot.AsUnsavedFiles();
|
2018-01-12 17:37:33 +00:00
|
|
|
timer.ResetAndPrint("[complete] Creating WorkingFile snapshot");
|
|
|
|
|
2018-04-14 16:52:17 +00:00
|
|
|
unsigned const kCompleteOptions =
|
|
|
|
CXCodeComplete_IncludeMacros | CXCodeComplete_IncludeBriefComments;
|
|
|
|
CXCodeCompleteResults* cx_results = clang_codeCompleteAt(
|
|
|
|
session->completion.tu->cx_tu, session->file.filename.c_str(),
|
|
|
|
request->position.line + 1, request->position.character + 1,
|
|
|
|
unsaved.data(), (unsigned)unsaved.size(), kCompleteOptions);
|
|
|
|
timer.ResetAndPrint("[complete] clangCodeCompleteAt");
|
|
|
|
if (!cx_results) {
|
|
|
|
request->on_complete({}, false /*is_cached_result*/);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<lsCompletionItem> ls_result;
|
|
|
|
// this is a guess but can be larger in case of std::optional
|
|
|
|
// parameters, as they may be expanded into multiple items
|
|
|
|
ls_result.reserve(cx_results->NumResults);
|
|
|
|
|
|
|
|
timer.Reset();
|
|
|
|
for (unsigned i = 0; i < cx_results->NumResults; ++i) {
|
|
|
|
CXCompletionResult& result = cx_results->Results[i];
|
|
|
|
|
|
|
|
// TODO: Try to figure out how we can hide base method calls without
|
|
|
|
// also hiding method implementation assistance, ie,
|
|
|
|
//
|
|
|
|
// void Foo::* {
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
|
|
|
|
if (clang_getCompletionAvailability(result.CompletionString) ==
|
|
|
|
CXAvailability_NotAvailable)
|
2017-09-22 02:25:33 +00:00
|
|
|
continue;
|
2017-07-12 18:25:58 +00:00
|
|
|
|
2018-04-14 16:52:17 +00:00
|
|
|
// TODO: fill in more data
|
|
|
|
lsCompletionItem ls_completion_item;
|
2017-12-30 07:15:46 +00:00
|
|
|
|
2018-04-14 16:52:17 +00:00
|
|
|
ls_completion_item.kind = GetCompletionKind(result.CursorKind);
|
|
|
|
ls_completion_item.documentation =
|
|
|
|
ToString(clang_getCompletionBriefComment(result.CompletionString));
|
2017-09-22 02:25:33 +00:00
|
|
|
|
2018-04-14 16:52:17 +00:00
|
|
|
// label/detail/filterText/insertText/priority
|
|
|
|
if (g_config->completion.detailedLabel) {
|
|
|
|
ls_completion_item.detail = ToString(
|
|
|
|
clang_getCompletionParent(result.CompletionString, nullptr));
|
2017-04-17 01:22:59 +00:00
|
|
|
|
2018-04-14 16:52:17 +00:00
|
|
|
auto first_idx = ls_result.size();
|
|
|
|
ls_result.push_back(ls_completion_item);
|
2017-05-20 06:35:14 +00:00
|
|
|
|
2018-04-14 16:52:17 +00:00
|
|
|
// label/filterText/insertText
|
|
|
|
BuildCompletionItemTexts(ls_result, result.CompletionString,
|
|
|
|
g_config->client.snippetSupport);
|
2017-09-22 02:25:33 +00:00
|
|
|
|
2018-04-14 16:52:17 +00:00
|
|
|
for (auto i = first_idx; i < ls_result.size(); ++i) {
|
|
|
|
if (g_config->client.snippetSupport &&
|
|
|
|
ls_result[i].insertTextFormat == lsInsertTextFormat::Snippet) {
|
|
|
|
ls_result[i].insertText += "$0";
|
|
|
|
}
|
|
|
|
|
|
|
|
ls_result[i].priority_ =
|
|
|
|
GetCompletionPriority(result.CompletionString, result.CursorKind,
|
|
|
|
ls_result[i].filterText);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
bool do_insert = true;
|
|
|
|
int angle_stack = 0;
|
|
|
|
BuildDetailString(result.CompletionString, ls_completion_item.label,
|
|
|
|
ls_completion_item.detail,
|
|
|
|
ls_completion_item.insertText, do_insert,
|
|
|
|
ls_completion_item.insertTextFormat,
|
|
|
|
&ls_completion_item.parameters_,
|
|
|
|
g_config->client.snippetSupport, angle_stack);
|
|
|
|
if (g_config->client.snippetSupport &&
|
|
|
|
ls_completion_item.insertTextFormat ==
|
|
|
|
lsInsertTextFormat::Snippet) {
|
|
|
|
ls_completion_item.insertText += "$0";
|
|
|
|
}
|
|
|
|
ls_completion_item.priority_ =
|
|
|
|
GetCompletionPriority(result.CompletionString, result.CursorKind,
|
|
|
|
ls_completion_item.label);
|
|
|
|
ls_result.push_back(ls_completion_item);
|
2017-07-16 00:08:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-14 16:52:17 +00:00
|
|
|
timer.ResetAndPrint("[complete] Building " +
|
|
|
|
std::to_string(ls_result.size()) +
|
|
|
|
" completion results");
|
|
|
|
|
|
|
|
request->on_complete(ls_result, false /*is_cached_result*/);
|
|
|
|
|
|
|
|
// Make sure |ls_results| is destroyed before clearing |cx_results|.
|
|
|
|
clang_disposeCodeCompleteResults(cx_results);
|
2017-04-17 01:22:59 +00:00
|
|
|
}
|
2017-03-26 21:40:34 +00:00
|
|
|
}
|
|
|
|
|
2018-04-14 16:52:17 +00:00
|
|
|
void DiagnosticQueryMain(ClangCompleteManager* completion_manager) {
|
|
|
|
while (true) {
|
|
|
|
// Fetching the completion request blocks until we have a request.
|
|
|
|
ClangCompleteManager::DiagnosticRequest request =
|
|
|
|
completion_manager->diagnostic_request_.Dequeue();
|
2018-05-09 05:01:58 +00:00
|
|
|
if (!g_config->diagnostics.onType)
|
|
|
|
continue;
|
2018-04-14 16:52:17 +00:00
|
|
|
std::string path = request.document.uri.GetPath();
|
2017-04-17 01:22:59 +00:00
|
|
|
|
2018-04-14 16:52:17 +00:00
|
|
|
std::shared_ptr<CompletionSession> session =
|
|
|
|
completion_manager->TryGetSession(path, true /*mark_as_completion*/,
|
|
|
|
true /*create_if_needed*/);
|
2017-05-10 04:52:15 +00:00
|
|
|
|
2018-04-14 16:52:17 +00:00
|
|
|
// At this point, we must have a translation unit. Block until we have one.
|
|
|
|
std::lock_guard<std::mutex> lock(session->diagnostics.lock);
|
|
|
|
Timer timer;
|
|
|
|
TryEnsureDocumentParsed(
|
|
|
|
completion_manager, session, &session->diagnostics.tu,
|
|
|
|
&session->diagnostics.index, false /*emit_diagnostics*/);
|
|
|
|
timer.ResetAndPrint("[diagnostics] TryEnsureDocumentParsed");
|
2017-05-10 04:52:15 +00:00
|
|
|
|
2018-04-14 16:52:17 +00:00
|
|
|
// It is possible we failed to create the document despite
|
|
|
|
// |TryEnsureDocumentParsed|.
|
|
|
|
if (!session->diagnostics.tu)
|
|
|
|
continue;
|
2017-09-22 01:14:57 +00:00
|
|
|
|
2018-04-14 16:52:17 +00:00
|
|
|
timer.Reset();
|
|
|
|
WorkingFiles::Snapshot snapshot =
|
|
|
|
completion_manager->working_files_->AsSnapshot({StripFileType(path)});
|
|
|
|
std::vector<CXUnsavedFile> unsaved = snapshot.AsUnsavedFiles();
|
|
|
|
timer.ResetAndPrint("[diagnostics] Creating WorkingFile snapshot");
|
|
|
|
|
|
|
|
// Emit diagnostics.
|
|
|
|
timer.Reset();
|
|
|
|
session->diagnostics.tu = ClangTranslationUnit::Reparse(
|
|
|
|
std::move(session->diagnostics.tu), unsaved);
|
|
|
|
timer.ResetAndPrint("[diagnostics] clang_reparseTranslationUnit");
|
|
|
|
if (!session->diagnostics.tu) {
|
|
|
|
LOG_S(ERROR) << "Reparsing translation unit for diagnostics failed for "
|
|
|
|
<< path;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t num_diagnostics =
|
|
|
|
clang_getNumDiagnostics(session->diagnostics.tu->cx_tu);
|
|
|
|
std::vector<lsDiagnostic> ls_diagnostics;
|
|
|
|
ls_diagnostics.reserve(num_diagnostics);
|
|
|
|
for (unsigned i = 0; i < num_diagnostics; ++i) {
|
|
|
|
CXDiagnostic cx_diag =
|
|
|
|
clang_getDiagnostic(session->diagnostics.tu->cx_tu, i);
|
|
|
|
std::optional<lsDiagnostic> diagnostic =
|
|
|
|
BuildAndDisposeDiagnostic(cx_diag, path);
|
|
|
|
// Filter messages like "too many errors emitted, stopping now
|
|
|
|
// [-ferror-limit=]" which has line = 0 and got subtracted by 1 after
|
|
|
|
// conversion to lsDiagnostic
|
|
|
|
if (diagnostic && diagnostic->range.start.line >= 0)
|
|
|
|
ls_diagnostics.push_back(*diagnostic);
|
|
|
|
}
|
|
|
|
completion_manager->on_diagnostic_(session->file.filename, ls_diagnostics);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace
|
2018-02-18 17:15:39 +00:00
|
|
|
|
2018-04-04 06:05:41 +00:00
|
|
|
ClangCompleteManager::ClangCompleteManager(Project* project,
|
2017-09-22 01:14:57 +00:00
|
|
|
WorkingFiles* working_files,
|
2017-09-27 06:03:43 +00:00
|
|
|
OnDiagnostic on_diagnostic,
|
2018-02-22 07:13:42 +00:00
|
|
|
OnDropped on_dropped)
|
2018-04-04 06:05:41 +00:00
|
|
|
: project_(project),
|
2017-09-22 01:14:57 +00:00
|
|
|
working_files_(working_files),
|
|
|
|
on_diagnostic_(on_diagnostic),
|
2018-02-22 07:13:42 +00:00
|
|
|
on_dropped_(on_dropped),
|
2017-10-23 07:28:21 +00:00
|
|
|
preloaded_sessions_(kMaxPreloadedSessions),
|
|
|
|
completion_sessions_(kMaxCompletionSessions) {
|
2018-04-16 19:36:02 +00:00
|
|
|
std::thread([&]() {
|
2018-05-27 19:24:56 +00:00
|
|
|
set_thread_name("comp-query");
|
2017-05-11 06:25:41 +00:00
|
|
|
CompletionQueryMain(this);
|
2018-04-16 19:36:02 +00:00
|
|
|
}).detach();
|
|
|
|
std::thread([&]() {
|
2018-05-27 19:24:56 +00:00
|
|
|
set_thread_name("comp-preload");
|
2018-04-14 16:52:17 +00:00
|
|
|
CompletionPreloadMain(this);
|
2018-04-16 19:36:02 +00:00
|
|
|
}).detach();
|
|
|
|
std::thread([&]() {
|
2018-05-27 19:24:56 +00:00
|
|
|
set_thread_name("diag-query");
|
2018-04-14 16:52:17 +00:00
|
|
|
DiagnosticQueryMain(this);
|
2018-04-16 19:36:02 +00:00
|
|
|
}).detach();
|
2017-04-17 01:22:59 +00:00
|
|
|
}
|
2017-03-26 21:40:34 +00:00
|
|
|
|
2017-09-22 01:14:57 +00:00
|
|
|
void ClangCompleteManager::CodeComplete(
|
2018-02-22 07:13:42 +00:00
|
|
|
const lsRequestId& id,
|
2017-09-22 01:14:57 +00:00
|
|
|
const lsTextDocumentPositionParams& completion_location,
|
|
|
|
const OnComplete& on_complete) {
|
2018-03-10 23:40:27 +00:00
|
|
|
completion_request_.PushBack(std::make_unique<CompletionRequest>(
|
2018-02-22 07:13:42 +00:00
|
|
|
id, completion_location.textDocument, completion_location.position,
|
2018-04-14 16:52:17 +00:00
|
|
|
on_complete));
|
2017-09-22 02:25:33 +00:00
|
|
|
}
|
2017-05-26 06:40:38 +00:00
|
|
|
|
2017-09-22 02:25:33 +00:00
|
|
|
void ClangCompleteManager::DiagnosticsUpdate(
|
|
|
|
const lsTextDocumentIdentifier& document) {
|
2018-04-14 16:52:17 +00:00
|
|
|
bool has = false;
|
|
|
|
diagnostic_request_.Iterate([&](const DiagnosticRequest& request) {
|
|
|
|
if (request.document.uri == document.uri)
|
|
|
|
has = true;
|
|
|
|
});
|
|
|
|
if (!has)
|
|
|
|
diagnostic_request_.PushBack(DiagnosticRequest{document},
|
|
|
|
true /*priority*/);
|
2017-03-26 21:40:34 +00:00
|
|
|
}
|
|
|
|
|
2017-05-27 04:21:00 +00:00
|
|
|
void ClangCompleteManager::NotifyView(const std::string& filename) {
|
2017-05-26 06:40:38 +00:00
|
|
|
//
|
|
|
|
// On view, we reparse only if the file has not been parsed. The existence of
|
|
|
|
// a CompletionSession instance implies the file is already parsed or will be
|
|
|
|
// parsed soon.
|
|
|
|
//
|
2017-03-26 21:40:34 +00:00
|
|
|
|
2017-10-23 07:28:21 +00:00
|
|
|
// Only reparse the file if we create a new CompletionSession.
|
|
|
|
if (EnsureCompletionOrCreatePreloadSession(filename))
|
2018-04-14 16:52:17 +00:00
|
|
|
preload_requests_.PushBack(PreloadRequest(filename), true);
|
2017-03-28 01:47:12 +00:00
|
|
|
}
|
2017-05-10 04:52:15 +00:00
|
|
|
|
2017-05-27 04:21:00 +00:00
|
|
|
void ClangCompleteManager::NotifyEdit(const std::string& filename) {
|
2017-05-26 06:40:38 +00:00
|
|
|
//
|
2017-10-23 07:28:21 +00:00
|
|
|
// We treat an edit like a view, because the completion logic will handle
|
|
|
|
// moving the CompletionSession instance from preloaded to completion
|
|
|
|
// storage.
|
2017-05-26 06:40:38 +00:00
|
|
|
//
|
2017-05-10 04:52:15 +00:00
|
|
|
|
2017-10-23 07:28:21 +00:00
|
|
|
NotifyView(filename);
|
2017-05-26 06:40:38 +00:00
|
|
|
}
|
|
|
|
|
2017-05-27 04:21:00 +00:00
|
|
|
void ClangCompleteManager::NotifySave(const std::string& filename) {
|
2017-05-26 06:40:38 +00:00
|
|
|
//
|
|
|
|
// On save, always reparse.
|
|
|
|
//
|
|
|
|
|
2017-10-23 07:28:21 +00:00
|
|
|
EnsureCompletionOrCreatePreloadSession(filename);
|
2018-04-14 16:52:17 +00:00
|
|
|
preload_requests_.PushBack(PreloadRequest(filename), true);
|
2017-05-26 06:40:38 +00:00
|
|
|
}
|
|
|
|
|
2017-10-17 18:43:33 +00:00
|
|
|
void ClangCompleteManager::NotifyClose(const std::string& filename) {
|
|
|
|
//
|
|
|
|
// On close, we clear any existing CompletionSession instance.
|
|
|
|
//
|
|
|
|
|
|
|
|
std::lock_guard<std::mutex> lock(sessions_lock_);
|
|
|
|
|
|
|
|
// Take and drop. It's okay if we don't actually drop the file, it'll
|
|
|
|
// eventually get pushed out of the caches as the user opens other files.
|
2017-12-04 02:23:14 +00:00
|
|
|
auto preloaded_ptr = preloaded_sessions_.TryTake(filename);
|
2017-10-23 07:28:21 +00:00
|
|
|
LOG_IF_S(INFO, !!preloaded_ptr)
|
|
|
|
<< "Dropped preloaded-based code completion session for " << filename;
|
2017-12-04 02:23:14 +00:00
|
|
|
auto completion_ptr = completion_sessions_.TryTake(filename);
|
2017-10-23 07:28:21 +00:00
|
|
|
LOG_IF_S(INFO, !!completion_ptr)
|
|
|
|
<< "Dropped completion-based code completion session for " << filename;
|
|
|
|
|
|
|
|
// We should never have both a preloaded and completion session.
|
|
|
|
assert((preloaded_ptr && completion_ptr) == false);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ClangCompleteManager::EnsureCompletionOrCreatePreloadSession(
|
|
|
|
const std::string& filename) {
|
|
|
|
std::lock_guard<std::mutex> lock(sessions_lock_);
|
2017-10-23 15:38:01 +00:00
|
|
|
|
|
|
|
// Check for an existing CompletionSession.
|
2017-12-04 02:23:14 +00:00
|
|
|
if (preloaded_sessions_.TryGet(filename) ||
|
|
|
|
completion_sessions_.TryGet(filename)) {
|
2017-10-23 15:38:01 +00:00
|
|
|
return false;
|
2017-10-23 15:39:33 +00:00
|
|
|
}
|
2017-10-23 15:38:01 +00:00
|
|
|
|
|
|
|
// No CompletionSession, create new one.
|
|
|
|
auto session = std::make_shared<CompletionSession>(
|
|
|
|
project_->FindCompilationEntryForFile(filename), working_files_);
|
2017-12-04 02:23:14 +00:00
|
|
|
preloaded_sessions_.Insert(session->file.filename, session);
|
2017-10-23 15:38:01 +00:00
|
|
|
return true;
|
2017-10-17 18:43:33 +00:00
|
|
|
}
|
|
|
|
|
2017-10-23 07:28:21 +00:00
|
|
|
std::shared_ptr<CompletionSession> ClangCompleteManager::TryGetSession(
|
2017-09-22 01:14:57 +00:00
|
|
|
const std::string& filename,
|
2017-10-23 07:28:21 +00:00
|
|
|
bool mark_as_completion,
|
2017-09-22 01:14:57 +00:00
|
|
|
bool create_if_needed) {
|
2017-05-26 06:40:38 +00:00
|
|
|
std::lock_guard<std::mutex> lock(sessions_lock_);
|
|
|
|
|
2017-10-23 07:28:21 +00:00
|
|
|
// Try to find a preloaded session.
|
|
|
|
std::shared_ptr<CompletionSession> preloaded_session =
|
2017-12-04 02:23:14 +00:00
|
|
|
preloaded_sessions_.TryGet(filename);
|
2017-10-23 07:28:21 +00:00
|
|
|
|
|
|
|
if (preloaded_session) {
|
|
|
|
// If this request is for a completion, we should move it to
|
|
|
|
// |completion_sessions|.
|
|
|
|
if (mark_as_completion) {
|
2017-12-04 02:23:14 +00:00
|
|
|
preloaded_sessions_.TryTake(filename);
|
|
|
|
completion_sessions_.Insert(filename, preloaded_session);
|
2017-10-23 07:28:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return preloaded_session;
|
2017-10-23 04:49:17 +00:00
|
|
|
}
|
2017-06-10 01:02:48 +00:00
|
|
|
|
2017-10-23 07:28:21 +00:00
|
|
|
// Try to find a completion session. If none create one.
|
|
|
|
std::shared_ptr<CompletionSession> completion_session =
|
2017-12-04 02:23:14 +00:00
|
|
|
completion_sessions_.TryGet(filename);
|
2017-10-23 07:28:21 +00:00
|
|
|
if (!completion_session && create_if_needed) {
|
|
|
|
completion_session = std::make_shared<CompletionSession>(
|
2017-10-23 04:49:17 +00:00
|
|
|
project_->FindCompilationEntryForFile(filename), working_files_);
|
2017-12-04 02:23:14 +00:00
|
|
|
completion_sessions_.Insert(filename, completion_session);
|
2017-05-26 06:40:38 +00:00
|
|
|
}
|
2017-06-10 01:02:48 +00:00
|
|
|
|
2017-10-23 07:28:21 +00:00
|
|
|
return completion_session;
|
2017-06-10 01:02:48 +00:00
|
|
|
}
|
2018-03-20 12:33:02 +00:00
|
|
|
|
|
|
|
void ClangCompleteManager::FlushSession(const std::string& filename) {
|
|
|
|
std::lock_guard<std::mutex> lock(sessions_lock_);
|
|
|
|
|
|
|
|
preloaded_sessions_.TryTake(filename);
|
|
|
|
completion_sessions_.TryTake(filename);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ClangCompleteManager::FlushAllSessions() {
|
2018-05-27 19:24:56 +00:00
|
|
|
LOG_S(INFO) << "flush all clang complete sessions";
|
|
|
|
std::lock_guard<std::mutex> lock(sessions_lock_);
|
2018-03-20 12:33:02 +00:00
|
|
|
|
2018-05-27 19:24:56 +00:00
|
|
|
preloaded_sessions_.Clear();
|
|
|
|
completion_sessions_.Clear();
|
2018-03-20 12:33:02 +00:00
|
|
|
}
|
2018-03-31 08:01:32 +00:00
|
|
|
|
|
|
|
void CodeCompleteCache::WithLock(std::function<void()> action) {
|
|
|
|
std::lock_guard<std::mutex> lock(mutex_);
|
|
|
|
action();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CodeCompleteCache::IsCacheValid(lsTextDocumentPositionParams position) {
|
|
|
|
std::lock_guard<std::mutex> lock(mutex_);
|
|
|
|
return cached_path_ == position.textDocument.uri.GetPath() &&
|
|
|
|
cached_completion_position_ == position.position;
|
|
|
|
}
|