2018-08-21 05:27:52 +00:00
|
|
|
/* Copyright 2017-2018 ccls Authors
|
|
|
|
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
you may not use this file except in compliance with the License.
|
|
|
|
You may obtain a copy of the License at
|
|
|
|
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
See the License for the specific language governing permissions and
|
|
|
|
limitations under the License.
|
|
|
|
==============================================================================*/
|
|
|
|
|
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
|
|
|
|
2018-07-16 05:49:32 +00:00
|
|
|
#include <clang/Frontend/CompilerInstance.h>
|
|
|
|
#include <clang/Frontend/FrontendDiagnostic.h>
|
2018-08-28 05:42:40 +00:00
|
|
|
#include <clang/Lex/PreprocessorOptions.h>
|
2018-07-14 17:00:04 +00:00
|
|
|
#include <clang/Sema/CodeCompleteConsumer.h>
|
2018-05-27 19:24:56 +00:00
|
|
|
#include <llvm/ADT/Twine.h>
|
2018-07-15 07:57:48 +00:00
|
|
|
#include <llvm/Config/llvm-config.h>
|
2018-07-16 05:49:32 +00:00
|
|
|
#include <llvm/Support/CrashRecoveryContext.h>
|
2018-05-27 19:24:56 +00:00
|
|
|
#include <llvm/Support/Threading.h>
|
2018-07-14 17:00:04 +00:00
|
|
|
using namespace clang;
|
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
|
|
|
|
2018-08-29 05:49:53 +00:00
|
|
|
namespace ccls {
|
2017-03-26 21:40:34 +00:00
|
|
|
namespace {
|
2017-06-29 02:50:30 +00:00
|
|
|
|
2018-08-09 17:08:14 +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
|
|
|
}
|
|
|
|
|
2018-07-14 23:02:59 +00:00
|
|
|
unsigned GetCompletionPriority(const CodeCompletionString &CCS,
|
2017-12-07 19:53:48 +00:00
|
|
|
CXCursorKind result_kind,
|
2018-07-14 23:02:59 +00:00
|
|
|
const std::optional<std::string> &typedText) {
|
2018-07-14 17:00:04 +00:00
|
|
|
unsigned priority = CCS.getPriority();
|
|
|
|
if (CCS.getAvailability() != CXAvailability_Available ||
|
|
|
|
result_kind == CXCursor_Destructor ||
|
|
|
|
result_kind == CXCursor_ConversionFunction ||
|
2018-02-07 13:01:42 +00:00
|
|
|
(result_kind == CXCursor_CXXMethod && typedText &&
|
2018-07-14 17:00:04 +00:00
|
|
|
StartsWith(*typedText, "operator")))
|
2017-05-10 04:52:15 +00:00
|
|
|
priority *= 100;
|
|
|
|
return priority;
|
2017-03-26 21:40:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
lsCompletionItemKind GetCompletionKind(CXCursorKind cursor_kind) {
|
|
|
|
switch (cursor_kind) {
|
2018-08-09 17:08:14 +00:00
|
|
|
case CXCursor_UnexposedDecl:
|
|
|
|
return lsCompletionItemKind::Text;
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
case CXCursor_ObjCInstanceMethodDecl:
|
|
|
|
case CXCursor_CXXMethod:
|
|
|
|
case CXCursor_ObjCClassMethodDecl:
|
|
|
|
return lsCompletionItemKind::Method;
|
|
|
|
|
|
|
|
case CXCursor_FunctionTemplate:
|
|
|
|
return lsCompletionItemKind::Function;
|
|
|
|
|
|
|
|
case CXCursor_Constructor:
|
|
|
|
case CXCursor_Destructor:
|
|
|
|
case CXCursor_ConversionFunction:
|
|
|
|
return lsCompletionItemKind::Constructor;
|
|
|
|
|
|
|
|
case CXCursor_ObjCIvarDecl:
|
|
|
|
return lsCompletionItemKind::Variable;
|
|
|
|
|
|
|
|
case CXCursor_ClassTemplate:
|
|
|
|
case CXCursor_ClassTemplatePartialSpecialization:
|
|
|
|
case CXCursor_UsingDeclaration:
|
|
|
|
case CXCursor_TypedefDecl:
|
|
|
|
case CXCursor_TypeAliasDecl:
|
|
|
|
case CXCursor_TypeAliasTemplateDecl:
|
|
|
|
case CXCursor_ObjCCategoryDecl:
|
|
|
|
case CXCursor_ObjCProtocolDecl:
|
|
|
|
case CXCursor_ObjCImplementationDecl:
|
|
|
|
case CXCursor_ObjCCategoryImplDecl:
|
|
|
|
return lsCompletionItemKind::Class;
|
|
|
|
|
|
|
|
case CXCursor_ObjCPropertyDecl:
|
|
|
|
return lsCompletionItemKind::Property;
|
|
|
|
|
|
|
|
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:
|
|
|
|
case CXCursor_ObjCSuperClassRef:
|
|
|
|
case CXCursor_ObjCProtocolRef:
|
|
|
|
case CXCursor_ObjCClassRef:
|
|
|
|
return lsCompletionItemKind::Reference;
|
|
|
|
|
|
|
|
// return lsCompletionItemKind::Unit;
|
|
|
|
// return lsCompletionItemKind::Value;
|
|
|
|
// return lsCompletionItemKind::Keyword;
|
|
|
|
// return lsCompletionItemKind::Snippet;
|
|
|
|
// return lsCompletionItemKind::Color;
|
|
|
|
// return lsCompletionItemKind::File;
|
|
|
|
|
|
|
|
case CXCursor_NotImplemented:
|
|
|
|
case CXCursor_OverloadCandidate:
|
|
|
|
return lsCompletionItemKind::Text;
|
|
|
|
|
|
|
|
case CXCursor_TemplateTypeParameter:
|
|
|
|
case CXCursor_TemplateTemplateParameter:
|
|
|
|
return lsCompletionItemKind::TypeParameter;
|
|
|
|
|
|
|
|
default:
|
|
|
|
LOG_S(WARNING) << "Unhandled completion kind " << cursor_kind;
|
|
|
|
return lsCompletionItemKind::Text;
|
2017-03-26 21:40:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-14 17:00:04 +00:00
|
|
|
void BuildCompletionItemTexts(std::vector<lsCompletionItem> &out,
|
|
|
|
CodeCompletionString &CCS,
|
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
|
|
|
bool include_snippets) {
|
|
|
|
assert(!out.empty());
|
|
|
|
auto out_first = out.size() - 1;
|
|
|
|
|
|
|
|
std::string result_type;
|
|
|
|
|
2018-07-14 17:00:04 +00:00
|
|
|
for (unsigned i = 0, num_chunks = CCS.size(); i < num_chunks; ++i) {
|
|
|
|
const CodeCompletionString::Chunk &Chunk = CCS[i];
|
|
|
|
CodeCompletionString::ChunkKind Kind = Chunk.Kind;
|
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;
|
2018-07-14 17:00:04 +00:00
|
|
|
switch (Kind) {
|
2018-08-09 17:08:14 +00:00
|
|
|
case CodeCompletionString::CK_TypedText:
|
|
|
|
case CodeCompletionString::CK_Text:
|
|
|
|
case CodeCompletionString::CK_Placeholder:
|
|
|
|
case CodeCompletionString::CK_Informative:
|
|
|
|
if (Chunk.Text)
|
|
|
|
text = Chunk.Text;
|
|
|
|
for (auto i = out_first; i < out.size(); i++) {
|
|
|
|
// first TypedText is used for filtering
|
|
|
|
if (Kind == CodeCompletionString::CK_TypedText && !out[i].filterText)
|
|
|
|
out[i].filterText = text;
|
|
|
|
if (Kind == CodeCompletionString::CK_Placeholder)
|
|
|
|
out[i].parameters_.push_back(text);
|
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
|
|
|
}
|
2018-08-09 17:08:14 +00:00
|
|
|
break;
|
|
|
|
case CodeCompletionString::CK_ResultType:
|
|
|
|
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
|
|
|
|
// to be very reliable.
|
|
|
|
continue;
|
|
|
|
case CodeCompletionString::CK_Optional: {
|
|
|
|
// duplicate last element, the recursive call will complete it
|
|
|
|
out.push_back(out.back());
|
|
|
|
BuildCompletionItemTexts(out, *Chunk.Optional, include_snippets);
|
|
|
|
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
|
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
|
|
|
}
|
|
|
|
|
2018-07-14 23:02:59 +00:00
|
|
|
if (Kind != CodeCompletionString::CK_Informative)
|
|
|
|
for (auto i = out_first; i < out.size(); ++i) {
|
|
|
|
out[i].label += text;
|
|
|
|
if (!include_snippets && !out[i].parameters_.empty())
|
|
|
|
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
|
|
|
|
2018-07-14 23:02:59 +00:00
|
|
|
if (Kind == CodeCompletionString::CK_Placeholder) {
|
2018-08-09 17:08:14 +00:00
|
|
|
out[i].insertText += "${" +
|
|
|
|
std::to_string(out[i].parameters_.size()) + ":" +
|
|
|
|
text + "}";
|
2018-07-14 23:02:59 +00:00
|
|
|
out[i].insertTextFormat = lsInsertTextFormat::Snippet;
|
|
|
|
} else {
|
|
|
|
out[i].insertText += text;
|
|
|
|
}
|
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
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-14 23:02:59 +00:00
|
|
|
if (result_type.size())
|
|
|
|
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;
|
|
|
|
}
|
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
|
|
|
}
|
|
|
|
|
2018-01-23 06:21:32 +00:00
|
|
|
// |do_insert|: if |!do_insert|, do not append strings to |insert| after
|
|
|
|
// a placeholder.
|
2018-07-14 17:00:04 +00:00
|
|
|
void BuildDetailString(const CodeCompletionString &CCS, lsCompletionItem &item,
|
|
|
|
bool &do_insert, std::vector<std::string> *parameters,
|
|
|
|
bool include_snippets, int &angle_stack) {
|
|
|
|
for (unsigned i = 0, num_chunks = CCS.size(); i < num_chunks; ++i) {
|
|
|
|
const CodeCompletionString::Chunk &Chunk = CCS[i];
|
|
|
|
CodeCompletionString::ChunkKind Kind = Chunk.Kind;
|
2018-08-09 17:08:14 +00:00
|
|
|
const char *text = nullptr;
|
2018-07-14 17:00:04 +00:00
|
|
|
switch (Kind) {
|
|
|
|
case CodeCompletionString::CK_TypedText:
|
|
|
|
item.label = Chunk.Text;
|
|
|
|
[[fallthrough]];
|
|
|
|
case CodeCompletionString::CK_Text:
|
|
|
|
item.detail += Chunk.Text;
|
|
|
|
if (do_insert)
|
|
|
|
item.insertText += Chunk.Text;
|
|
|
|
break;
|
|
|
|
case CodeCompletionString::CK_Placeholder: {
|
|
|
|
parameters->push_back(Chunk.Text);
|
|
|
|
item.detail += Chunk.Text;
|
|
|
|
// Add parameter declarations as snippets if enabled
|
|
|
|
if (include_snippets) {
|
2018-08-09 17:08:14 +00:00
|
|
|
item.insertText +=
|
|
|
|
"${" + std::to_string(parameters->size()) + ":" + Chunk.Text + "}";
|
2018-07-14 17:00:04 +00:00
|
|
|
item.insertTextFormat = lsInsertTextFormat::Snippet;
|
|
|
|
} else
|
|
|
|
do_insert = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case CodeCompletionString::CK_Informative:
|
|
|
|
item.detail += Chunk.Text;
|
|
|
|
break;
|
|
|
|
case CodeCompletionString::CK_Optional: {
|
|
|
|
// Do not add text to insert string if we're in angle brackets.
|
|
|
|
bool should_insert = do_insert && angle_stack == 0;
|
2018-08-09 17:08:14 +00:00
|
|
|
BuildDetailString(*Chunk.Optional, item, should_insert, parameters,
|
|
|
|
include_snippets, angle_stack);
|
2018-07-14 17:00:04 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case CodeCompletionString::CK_ResultType:
|
|
|
|
item.detail = Chunk.Text + item.detail + " ";
|
|
|
|
break;
|
|
|
|
case CodeCompletionString::CK_CurrentParameter:
|
|
|
|
// We have our own parsing logic for active parameter. This doesn't seem
|
|
|
|
// to be very reliable.
|
|
|
|
break;
|
|
|
|
// 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 = "<"; angle_stack++; break;
|
|
|
|
case CodeCompletionString::CK_RightAngle: text = ">"; angle_stack--; 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:
|
|
|
|
case CodeCompletionString::CK_VerticalSpace: text = " "; break;
|
|
|
|
// clang-format on
|
|
|
|
}
|
|
|
|
if (text) {
|
|
|
|
item.detail += text;
|
|
|
|
if (do_insert && include_snippets)
|
|
|
|
item.insertText += text;
|
2017-03-26 21:40:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-04-17 01:22:59 +00:00
|
|
|
|
2018-08-29 05:49:53 +00:00
|
|
|
bool LocationInRange(SourceLocation L, CharSourceRange R,
|
|
|
|
const SourceManager &M) {
|
|
|
|
assert(R.isCharRange());
|
|
|
|
if (!R.isValid() || M.getFileID(R.getBegin()) != M.getFileID(R.getEnd()) ||
|
|
|
|
M.getFileID(R.getBegin()) != M.getFileID(L))
|
|
|
|
return false;
|
|
|
|
return L != R.getEnd() && M.isPointWithin(L, R.getBegin(), R.getEnd());
|
|
|
|
}
|
|
|
|
|
|
|
|
CharSourceRange DiagnosticRange(const clang::Diagnostic &D, const LangOptions &L) {
|
|
|
|
auto &M = D.getSourceManager();
|
|
|
|
auto Loc = M.getFileLoc(D.getLocation());
|
|
|
|
// Accept the first range that contains the location.
|
|
|
|
for (const auto &CR : D.getRanges()) {
|
|
|
|
auto R = Lexer::makeFileCharRange(CR, M, L);
|
|
|
|
if (LocationInRange(Loc, R, M))
|
|
|
|
return R;
|
|
|
|
}
|
|
|
|
// The range may be given as a fixit hint instead.
|
|
|
|
for (const auto &F : D.getFixItHints()) {
|
|
|
|
auto R = Lexer::makeFileCharRange(F.RemoveRange, M, L);
|
|
|
|
if (LocationInRange(Loc, R, M))
|
|
|
|
return R;
|
|
|
|
}
|
|
|
|
// If no suitable range is found, just use the token at the location.
|
|
|
|
auto R = Lexer::makeFileCharRange(CharSourceRange::getTokenRange(Loc), M, L);
|
|
|
|
if (!R.isValid()) // Fall back to location only, let the editor deal with it.
|
|
|
|
R = CharSourceRange::getCharRange(Loc);
|
|
|
|
return R;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-07-14 23:02:59 +00:00
|
|
|
class CaptureCompletionResults : public CodeCompleteConsumer {
|
|
|
|
std::shared_ptr<clang::GlobalCodeCompletionAllocator> Alloc;
|
|
|
|
CodeCompletionTUInfo CCTUInfo;
|
|
|
|
|
|
|
|
public:
|
|
|
|
std::vector<lsCompletionItem> ls_items;
|
|
|
|
|
|
|
|
CaptureCompletionResults(const CodeCompleteOptions &Opts)
|
|
|
|
: CodeCompleteConsumer(Opts, false),
|
|
|
|
Alloc(std::make_shared<clang::GlobalCodeCompletionAllocator>()),
|
|
|
|
CCTUInfo(Alloc) {}
|
|
|
|
|
2018-08-09 17:08:14 +00:00
|
|
|
void ProcessCodeCompleteResults(Sema &S, CodeCompletionContext Context,
|
|
|
|
CodeCompletionResult *Results,
|
|
|
|
unsigned NumResults) override {
|
2018-07-14 23:02:59 +00:00
|
|
|
ls_items.reserve(NumResults);
|
|
|
|
for (unsigned i = 0; i != NumResults; i++) {
|
2018-08-30 04:20:27 +00:00
|
|
|
auto &R = Results[i];
|
|
|
|
if (R.Availability == CXAvailability_NotAccessible ||
|
|
|
|
R.Availability == CXAvailability_NotAvailable)
|
2018-08-28 05:42:40 +00:00
|
|
|
continue;
|
2018-08-30 04:20:27 +00:00
|
|
|
CodeCompletionString *CCS = R.CreateCodeCompletionString(
|
2018-07-14 23:02:59 +00:00
|
|
|
S, Context, getAllocator(), getCodeCompletionTUInfo(),
|
|
|
|
includeBriefComments());
|
|
|
|
lsCompletionItem ls_item;
|
2018-08-30 04:20:27 +00:00
|
|
|
ls_item.kind = GetCompletionKind(R.CursorKind);
|
2018-08-09 17:08:14 +00:00
|
|
|
if (const char *brief = CCS->getBriefComment())
|
2018-07-14 23:02:59 +00:00
|
|
|
ls_item.documentation = brief;
|
|
|
|
|
|
|
|
// label/detail/filterText/insertText/priority
|
|
|
|
if (g_config->completion.detailedLabel) {
|
|
|
|
ls_item.detail = CCS->getParentContextName().str();
|
|
|
|
|
|
|
|
size_t first_idx = ls_items.size();
|
|
|
|
ls_items.push_back(ls_item);
|
|
|
|
BuildCompletionItemTexts(ls_items, *CCS,
|
|
|
|
g_config->client.snippetSupport);
|
|
|
|
|
|
|
|
for (size_t j = first_idx; j < ls_items.size(); j++) {
|
|
|
|
if (g_config->client.snippetSupport &&
|
|
|
|
ls_items[j].insertTextFormat == lsInsertTextFormat::Snippet)
|
|
|
|
ls_items[j].insertText += "$0";
|
|
|
|
ls_items[j].priority_ = GetCompletionPriority(
|
2018-07-17 06:22:34 +00:00
|
|
|
*CCS, Results[i].CursorKind, ls_items[j].filterText);
|
2018-07-14 23:02:59 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
bool do_insert = true;
|
|
|
|
int angle_stack = 0;
|
2018-08-09 17:08:14 +00:00
|
|
|
BuildDetailString(*CCS, ls_item, do_insert, &ls_item.parameters_,
|
2018-07-14 23:02:59 +00:00
|
|
|
g_config->client.snippetSupport, angle_stack);
|
|
|
|
if (g_config->client.snippetSupport &&
|
|
|
|
ls_item.insertTextFormat == lsInsertTextFormat::Snippet)
|
|
|
|
ls_item.insertText += "$0";
|
|
|
|
ls_item.priority_ =
|
|
|
|
GetCompletionPriority(*CCS, Results[i].CursorKind, ls_item.label);
|
|
|
|
ls_items.push_back(ls_item);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-15 17:10:24 +00:00
|
|
|
void ProcessOverloadCandidates(Sema &S, unsigned CurrentArg,
|
|
|
|
OverloadCandidate *Candidates,
|
|
|
|
unsigned NumCandidates) override {}
|
|
|
|
|
2018-07-14 23:02:59 +00:00
|
|
|
CodeCompletionAllocator &getAllocator() override { return *Alloc; }
|
|
|
|
|
2018-08-09 17:08:14 +00:00
|
|
|
CodeCompletionTUInfo &getCodeCompletionTUInfo() override { return CCTUInfo; }
|
2018-07-14 23:02:59 +00:00
|
|
|
};
|
|
|
|
|
2018-08-29 05:49:53 +00:00
|
|
|
class StoreDiags : public DiagnosticConsumer {
|
|
|
|
const LangOptions *LangOpts;
|
|
|
|
std::optional<Diag> last;
|
|
|
|
std::vector<Diag> output;
|
|
|
|
void Flush() {
|
|
|
|
if (!last)
|
|
|
|
return;
|
|
|
|
bool mentions = last->inside_main || last->edits.size();
|
|
|
|
if (!mentions)
|
|
|
|
for (auto &N : last->notes)
|
|
|
|
if (N.inside_main)
|
|
|
|
mentions = true;
|
|
|
|
if (mentions)
|
|
|
|
output.push_back(std::move(*last));
|
|
|
|
last.reset();
|
|
|
|
}
|
|
|
|
public:
|
|
|
|
std::vector<Diag> Take() {
|
|
|
|
return std::move(output);
|
|
|
|
}
|
|
|
|
void BeginSourceFile(const LangOptions &Opts, const Preprocessor *) override {
|
|
|
|
LangOpts = &Opts;
|
|
|
|
}
|
|
|
|
void EndSourceFile() override {
|
|
|
|
Flush();
|
|
|
|
}
|
|
|
|
void HandleDiagnostic(DiagnosticsEngine::Level Level,
|
|
|
|
const Diagnostic &Info) override {
|
|
|
|
DiagnosticConsumer::HandleDiagnostic(Level, Info);
|
|
|
|
SourceLocation L = Info.getLocation();
|
|
|
|
if (!L.isValid()) return;
|
|
|
|
const SourceManager &SM = Info.getSourceManager();
|
|
|
|
bool inside_main = SM.isInMainFile(L);
|
|
|
|
auto fillDiagBase = [&](DiagBase &d) {
|
|
|
|
llvm::SmallString<64> Message;
|
|
|
|
Info.FormatDiagnostic(Message);
|
|
|
|
d.range =
|
|
|
|
FromCharSourceRange(SM, *LangOpts, DiagnosticRange(Info, *LangOpts));
|
|
|
|
d.message = Message.str();
|
|
|
|
d.inside_main = inside_main;
|
|
|
|
d.file = SM.getFilename(Info.getLocation());
|
|
|
|
d.level = Level;
|
|
|
|
d.category = DiagnosticIDs::getCategoryNumberForDiag(Info.getID());
|
|
|
|
};
|
|
|
|
|
|
|
|
auto addFix = [&](bool SyntheticMessage) -> bool {
|
|
|
|
if (!inside_main)
|
|
|
|
return false;
|
|
|
|
for (const FixItHint &FixIt : Info.getFixItHints()) {
|
|
|
|
if (!SM.isInMainFile(FixIt.RemoveRange.getBegin()))
|
|
|
|
return false;
|
|
|
|
lsTextEdit edit;
|
|
|
|
edit.newText = FixIt.CodeToInsert;
|
|
|
|
auto r = FromCharSourceRange(SM, *LangOpts, FixIt.RemoveRange);
|
|
|
|
edit.range =
|
|
|
|
lsRange{{r.start.line, r.start.column}, {r.end.line, r.end.column}};
|
|
|
|
last->edits.push_back(std::move(edit));
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
};
|
2017-05-11 06:25:41 +00:00
|
|
|
|
2018-08-29 05:49:53 +00:00
|
|
|
if (Level == DiagnosticsEngine::Note || Level == DiagnosticsEngine::Remark) {
|
|
|
|
if (Info.getFixItHints().size()) {
|
|
|
|
addFix(false);
|
|
|
|
} else {
|
|
|
|
Note &n = last->notes.emplace_back();
|
|
|
|
fillDiagBase(n);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
Flush();
|
|
|
|
last = Diag();
|
|
|
|
fillDiagBase(*last);
|
|
|
|
if (!Info.getFixItHints().empty())
|
|
|
|
addFix(true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
2017-05-11 06:25:41 +00:00
|
|
|
|
2018-08-28 05:42:40 +00:00
|
|
|
std::unique_ptr<CompilerInvocation>
|
|
|
|
buildCompilerInvocation(const std::vector<std::string> &args,
|
|
|
|
IntrusiveRefCntPtr<vfs::FileSystem> VFS) {
|
|
|
|
std::vector<const char *> cargs;
|
|
|
|
for (auto &arg : args)
|
|
|
|
cargs.push_back(arg.c_str());
|
|
|
|
IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
|
|
|
|
CompilerInstance::createDiagnostics(new DiagnosticOptions));
|
|
|
|
std::unique_ptr<CompilerInvocation> CI =
|
|
|
|
createInvocationFromCommandLine(cargs, Diags, VFS);
|
|
|
|
if (CI) {
|
2018-08-29 05:49:53 +00:00
|
|
|
CI->getFrontendOpts().DisableFree = false;
|
2018-08-28 05:42:40 +00:00
|
|
|
CI->getLangOpts()->CommentOpts.ParseAllComments = true;
|
|
|
|
CI->getLangOpts()->SpellChecking = false;
|
|
|
|
}
|
|
|
|
return CI;
|
|
|
|
}
|
|
|
|
|
2018-08-29 05:49:53 +00:00
|
|
|
std::unique_ptr<CompilerInstance>
|
|
|
|
BuildCompilerInstance(CompletionSession &session,
|
|
|
|
std::unique_ptr<CompilerInvocation> CI,
|
|
|
|
DiagnosticConsumer &DC,
|
|
|
|
const WorkingFiles::Snapshot &snapshot,
|
|
|
|
std::vector<std::unique_ptr<llvm::MemoryBuffer>> &Bufs) {
|
|
|
|
for (auto &file : snapshot.files) {
|
|
|
|
Bufs.push_back(llvm::MemoryBuffer::getMemBuffer(file.content));
|
|
|
|
if (file.filename == session.file.filename) {
|
|
|
|
if (auto Preamble = session.GetPreamble()) {
|
|
|
|
#if LLVM_VERSION_MAJOR >= 7
|
|
|
|
Preamble->Preamble.OverridePreamble(*CI, session.FS,
|
|
|
|
Bufs.back().get());
|
|
|
|
#else
|
2018-08-29 15:33:20 +00:00
|
|
|
Preamble->Preamble.AddImplicitPreamble(*CI, session.FS,
|
2018-08-29 05:49:53 +00:00
|
|
|
Bufs.back().get());
|
|
|
|
#endif
|
|
|
|
} else {
|
|
|
|
CI->getPreprocessorOpts().addRemappedFile(
|
|
|
|
CI->getFrontendOpts().Inputs[0].getFile(), Bufs.back().get());
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
CI->getPreprocessorOpts().addRemappedFile(file.filename,
|
|
|
|
Bufs.back().get());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
auto Clang = std::make_unique<CompilerInstance>(session.PCH);
|
|
|
|
Clang->setInvocation(std::move(CI));
|
|
|
|
Clang->setVirtualFileSystem(session.FS);
|
|
|
|
Clang->createDiagnostics(&DC, false);
|
|
|
|
Clang->setTarget(TargetInfo::CreateTargetInfo(
|
|
|
|
Clang->getDiagnostics(), Clang->getInvocation().TargetOpts));
|
|
|
|
if (!Clang->hasTarget())
|
|
|
|
return nullptr;
|
|
|
|
return Clang;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Parse(CompilerInstance &Clang) {
|
|
|
|
SyntaxOnlyAction Action;
|
|
|
|
if (!Action.BeginSourceFile(Clang, Clang.getFrontendOpts().Inputs[0]))
|
|
|
|
return false;
|
|
|
|
if (!Action.Execute())
|
|
|
|
return false;
|
|
|
|
Action.EndSourceFile();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-08-09 17:08:14 +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-08-28 05:42:40 +00:00
|
|
|
const auto &args = session->file.args;
|
|
|
|
WorkingFiles::Snapshot snapshot = session->wfiles->AsSnapshot(
|
|
|
|
{StripFileType(session->file.filename)});
|
2017-05-26 06:40:38 +00:00
|
|
|
|
2018-08-28 05:42:40 +00:00
|
|
|
LOG_S(INFO) << "create completion session for " << session->file.filename;
|
|
|
|
if (std::unique_ptr<CompilerInvocation> CI =
|
|
|
|
buildCompilerInvocation(args, session->FS))
|
|
|
|
session->BuildPreamble(*CI);
|
2017-05-11 06:25:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-09 17:08:14 +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-08-28 05:42:40 +00:00
|
|
|
std::unique_ptr<CompilerInvocation> CI =
|
|
|
|
buildCompilerInvocation(session->file.args, session->FS);
|
|
|
|
if (!CI)
|
|
|
|
continue;
|
|
|
|
clang::CodeCompleteOptions CCOpts;
|
|
|
|
#if LLVM_VERSION_MAJOR >= 7
|
|
|
|
CCOpts.IncludeFixIts = true;
|
|
|
|
#endif
|
|
|
|
CCOpts.IncludeCodePatterns = true;
|
|
|
|
auto &FOpts = CI->getFrontendOpts();
|
|
|
|
FOpts.CodeCompleteOpts = CCOpts;
|
|
|
|
FOpts.CodeCompletionAt.FileName = session->file.filename;
|
|
|
|
FOpts.CodeCompletionAt.Line = request->position.line + 1;
|
|
|
|
FOpts.CodeCompletionAt.Column = request->position.character + 1;
|
2017-10-23 05:07:50 +00:00
|
|
|
|
2018-08-29 05:49:53 +00:00
|
|
|
StoreDiags DC;
|
2018-08-28 05:42:40 +00:00
|
|
|
WorkingFiles::Snapshot snapshot =
|
|
|
|
completion_manager->working_files_->AsSnapshot({StripFileType(path)});
|
|
|
|
std::vector<std::unique_ptr<llvm::MemoryBuffer>> Bufs;
|
2018-08-29 05:49:53 +00:00
|
|
|
auto Clang = BuildCompilerInstance(*session, std::move(CI), DC, snapshot, Bufs);
|
|
|
|
if (!Clang)
|
|
|
|
continue;
|
2018-08-28 05:42:40 +00:00
|
|
|
|
|
|
|
auto Consumer = new CaptureCompletionResults(CCOpts);
|
|
|
|
Clang->setCodeCompletionConsumer(Consumer);
|
2018-08-29 05:49:53 +00:00
|
|
|
if (!Parse(*Clang))
|
2018-08-28 05:42:40 +00:00
|
|
|
continue;
|
|
|
|
for (auto &Buf : Bufs)
|
|
|
|
Buf.release();
|
|
|
|
|
|
|
|
request->on_complete(Consumer->ls_items, false /*is_cached_result*/);
|
2017-04-17 01:22:59 +00:00
|
|
|
}
|
2017-03-26 21:40:34 +00:00
|
|
|
}
|
|
|
|
|
2018-07-15 17:10:24 +00:00
|
|
|
void DiagnosticQueryMain(ClangCompleteManager *manager) {
|
2018-04-14 16:52:17 +00:00
|
|
|
while (true) {
|
|
|
|
// Fetching the completion request blocks until we have a request.
|
|
|
|
ClangCompleteManager::DiagnosticRequest request =
|
2018-07-15 17:10:24 +00:00
|
|
|
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-07-15 17:10:24 +00:00
|
|
|
std::shared_ptr<CompletionSession> session = manager->TryGetSession(
|
|
|
|
path, true /*mark_as_completion*/, true /*create_if_needed*/);
|
2017-05-10 04:52:15 +00:00
|
|
|
|
2018-08-29 05:49:53 +00:00
|
|
|
std::unique_ptr<CompilerInvocation> CI =
|
|
|
|
buildCompilerInvocation(session->file.args, session->FS);
|
|
|
|
if (!CI)
|
2018-04-14 16:52:17 +00:00
|
|
|
continue;
|
2018-08-29 05:49:53 +00:00
|
|
|
StoreDiags DC;
|
2018-04-14 16:52:17 +00:00
|
|
|
WorkingFiles::Snapshot snapshot =
|
2018-07-15 17:10:24 +00:00
|
|
|
manager->working_files_->AsSnapshot({StripFileType(path)});
|
2018-08-29 05:49:53 +00:00
|
|
|
std::vector<std::unique_ptr<llvm::MemoryBuffer>> Bufs;
|
|
|
|
auto Clang = BuildCompilerInstance(*session, std::move(CI), DC, snapshot, Bufs);
|
|
|
|
if (!Clang)
|
2018-04-14 16:52:17 +00:00
|
|
|
continue;
|
2018-08-29 05:49:53 +00:00
|
|
|
if (!Parse(*Clang))
|
|
|
|
continue;
|
|
|
|
for (auto &Buf : Bufs)
|
|
|
|
Buf.release();
|
2018-04-14 16:52:17 +00:00
|
|
|
|
2018-07-14 23:02:59 +00:00
|
|
|
std::vector<lsDiagnostic> ls_diags;
|
2018-08-29 05:49:53 +00:00
|
|
|
for (auto &d : DC.Take()) {
|
|
|
|
if (!d.inside_main)
|
2018-08-09 17:08:14 +00:00
|
|
|
continue;
|
2018-08-29 05:49:53 +00:00
|
|
|
lsDiagnostic &ls_diag = ls_diags.emplace_back();
|
|
|
|
ls_diag.range = lsRange{{d.range.start.line, d.range.start.column},
|
|
|
|
{d.range.end.line, d.range.end.column}};
|
|
|
|
ls_diag.message = d.message;
|
|
|
|
switch (d.level) {
|
2018-07-14 23:02:59 +00:00
|
|
|
case DiagnosticsEngine::Ignored:
|
|
|
|
// llvm_unreachable
|
|
|
|
case DiagnosticsEngine::Note:
|
|
|
|
case DiagnosticsEngine::Remark:
|
|
|
|
ls_diag.severity = lsDiagnosticSeverity::Information;
|
2018-07-15 17:10:24 +00:00
|
|
|
continue;
|
2018-07-14 23:02:59 +00:00
|
|
|
case DiagnosticsEngine::Warning:
|
|
|
|
ls_diag.severity = lsDiagnosticSeverity::Warning;
|
|
|
|
break;
|
|
|
|
case DiagnosticsEngine::Error:
|
|
|
|
case DiagnosticsEngine::Fatal:
|
|
|
|
ls_diag.severity = lsDiagnosticSeverity::Error;
|
|
|
|
}
|
2018-08-29 05:49:53 +00:00
|
|
|
ls_diag.code = d.category;
|
|
|
|
ls_diag.fixits_ = d.edits;
|
2018-04-14 16:52:17 +00:00
|
|
|
}
|
2018-07-15 17:10:24 +00:00
|
|
|
manager->on_diagnostic_(path, ls_diags);
|
2018-04-14 16:52:17 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-09 17:08:14 +00:00
|
|
|
} // namespace
|
2018-02-18 17:15:39 +00:00
|
|
|
|
2018-08-28 05:42:40 +00:00
|
|
|
std::shared_ptr<PreambleData> CompletionSession::GetPreamble() {
|
2018-08-29 05:49:53 +00:00
|
|
|
std::lock_guard<std::mutex> lock(mutex);
|
|
|
|
return preamble;
|
2018-08-28 05:42:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void CompletionSession::BuildPreamble(CompilerInvocation &CI) {
|
|
|
|
std::shared_ptr<PreambleData> OldP = GetPreamble();
|
2018-08-29 05:49:53 +00:00
|
|
|
std::string content = wfiles->GetContent(file.filename);
|
2018-08-28 05:42:40 +00:00
|
|
|
std::unique_ptr<llvm::MemoryBuffer> Buf =
|
2018-08-29 05:49:53 +00:00
|
|
|
llvm::MemoryBuffer::getMemBuffer(content);
|
2018-08-28 05:42:40 +00:00
|
|
|
auto Bounds = ComputePreambleBounds(*CI.getLangOpts(), Buf.get(), 0);
|
|
|
|
if (OldP && OldP->Preamble.CanReuse(CI, Buf.get(), Bounds, FS.get()))
|
|
|
|
return;
|
|
|
|
CI.getFrontendOpts().SkipFunctionBodies = true;
|
|
|
|
#if LLVM_VERSION_MAJOR >= 7
|
|
|
|
CI.getPreprocessorOpts().WriteCommentListToPCH = false;
|
|
|
|
#endif
|
|
|
|
|
2018-08-29 05:49:53 +00:00
|
|
|
StoreDiags DC;
|
|
|
|
IntrusiveRefCntPtr<DiagnosticsEngine> DE =
|
|
|
|
CompilerInstance::createDiagnostics(&CI.getDiagnosticOpts(), &DC, false);
|
2018-08-28 05:42:40 +00:00
|
|
|
PreambleCallbacks PP;
|
2018-08-29 05:49:53 +00:00
|
|
|
if (auto NewPreamble = PrecompiledPreamble::Build(CI, Buf.get(), Bounds,
|
|
|
|
*DE, FS, PCH, true, PP)) {
|
|
|
|
std::lock_guard<std::mutex> lock(mutex);
|
|
|
|
preamble =
|
|
|
|
std::make_shared<PreambleData>(std::move(*NewPreamble), DC.Take());
|
2018-08-28 05:42:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-29 05:49:53 +00:00
|
|
|
} // namespace ccls
|
|
|
|
|
2018-08-09 17:08:14 +00:00
|
|
|
ClangCompleteManager::ClangCompleteManager(Project *project,
|
|
|
|
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-08-09 17:08:14 +00:00
|
|
|
: project_(project), working_files_(working_files),
|
|
|
|
on_diagnostic_(on_diagnostic), on_dropped_(on_dropped),
|
2017-10-23 07:28:21 +00:00
|
|
|
preloaded_sessions_(kMaxPreloadedSessions),
|
2018-08-28 05:42:40 +00:00
|
|
|
completion_sessions_(kMaxCompletionSessions),
|
|
|
|
PCH(std::make_shared<PCHContainerOperations>()) {
|
2018-04-16 19:36:02 +00:00
|
|
|
std::thread([&]() {
|
2018-05-27 19:24:56 +00:00
|
|
|
set_thread_name("comp-query");
|
2018-08-29 05:49:53 +00:00
|
|
|
ccls::CompletionQueryMain(this);
|
2018-08-09 17:08:14 +00:00
|
|
|
})
|
|
|
|
.detach();
|
2018-04-16 19:36:02 +00:00
|
|
|
std::thread([&]() {
|
2018-05-27 19:24:56 +00:00
|
|
|
set_thread_name("comp-preload");
|
2018-08-29 05:49:53 +00:00
|
|
|
ccls::CompletionPreloadMain(this);
|
2018-08-09 17:08:14 +00:00
|
|
|
})
|
|
|
|
.detach();
|
2018-04-16 19:36:02 +00:00
|
|
|
std::thread([&]() {
|
2018-05-27 19:24:56 +00:00
|
|
|
set_thread_name("diag-query");
|
2018-08-29 05:49:53 +00:00
|
|
|
ccls::DiagnosticQueryMain(this);
|
2018-08-09 17:08:14 +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-08-09 17:08:14 +00:00
|
|
|
const lsRequestId &id,
|
|
|
|
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(
|
2018-08-09 17:08:14 +00:00
|
|
|
const lsTextDocumentIdentifier &document) {
|
2018-04-14 16:52:17 +00:00
|
|
|
bool has = false;
|
2018-08-09 17:08:14 +00:00
|
|
|
diagnostic_request_.Iterate([&](const DiagnosticRequest &request) {
|
2018-04-14 16:52:17 +00:00
|
|
|
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
|
|
|
}
|
|
|
|
|
2018-08-09 17:08:14 +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
|
|
|
|
2018-08-09 17:08:14 +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
|
|
|
}
|
|
|
|
|
2018-08-09 17:08:14 +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
|
|
|
}
|
|
|
|
|
2018-08-09 17:08:14 +00:00
|
|
|
void ClangCompleteManager::NotifyClose(const std::string &filename) {
|
2017-10-17 18:43:33 +00:00
|
|
|
//
|
|
|
|
// 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(
|
2018-08-09 17:08:14 +00:00
|
|
|
const std::string &filename) {
|
2017-10-23 07:28:21 +00:00
|
|
|
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.
|
2018-08-29 05:49:53 +00:00
|
|
|
auto session = std::make_shared<ccls::CompletionSession>(
|
2018-08-28 05:42:40 +00:00
|
|
|
project_->FindCompilationEntryForFile(filename), working_files_, PCH);
|
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
|
|
|
}
|
|
|
|
|
2018-08-29 05:49:53 +00:00
|
|
|
std::shared_ptr<ccls::CompletionSession>
|
2018-08-09 17:08:14 +00:00
|
|
|
ClangCompleteManager::TryGetSession(const std::string &filename,
|
|
|
|
bool mark_as_completion,
|
|
|
|
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.
|
2018-08-29 05:49:53 +00:00
|
|
|
std::shared_ptr<ccls::CompletionSession> preloaded =
|
2017-12-04 02:23:14 +00:00
|
|
|
preloaded_sessions_.TryGet(filename);
|
2017-10-23 07:28:21 +00:00
|
|
|
|
2018-08-29 05:49:53 +00:00
|
|
|
if (preloaded) {
|
2017-10-23 07:28:21 +00:00
|
|
|
// 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);
|
2018-08-29 05:49:53 +00:00
|
|
|
completion_sessions_.Insert(filename, preloaded);
|
2017-10-23 07:28:21 +00:00
|
|
|
}
|
2018-08-29 05:49:53 +00:00
|
|
|
return preloaded;
|
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.
|
2018-08-29 05:49:53 +00:00
|
|
|
std::shared_ptr<ccls::CompletionSession> session =
|
2017-12-04 02:23:14 +00:00
|
|
|
completion_sessions_.TryGet(filename);
|
2018-08-29 05:49:53 +00:00
|
|
|
if (!session && create_if_needed) {
|
|
|
|
session = std::make_shared<ccls::CompletionSession>(
|
2018-08-28 05:42:40 +00:00
|
|
|
project_->FindCompilationEntryForFile(filename), working_files_, PCH);
|
2018-08-29 05:49:53 +00:00
|
|
|
completion_sessions_.Insert(filename, session);
|
2017-05-26 06:40:38 +00:00
|
|
|
}
|
2017-06-10 01:02:48 +00:00
|
|
|
|
2018-08-29 05:49:53 +00:00
|
|
|
return session;
|
2017-06-10 01:02:48 +00:00
|
|
|
}
|
2018-03-20 12:33:02 +00:00
|
|
|
|
2018-08-09 17:08:14 +00:00
|
|
|
void ClangCompleteManager::FlushSession(const std::string &filename) {
|
|
|
|
std::lock_guard<std::mutex> lock(sessions_lock_);
|
2018-03-20 12:33:02 +00:00
|
|
|
|
2018-08-09 17:08:14 +00:00
|
|
|
preloaded_sessions_.TryTake(filename);
|
|
|
|
completion_sessions_.TryTake(filename);
|
2018-03-20 12:33:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|