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"
|
2017-03-26 21:40:34 +00:00
|
|
|
#include "libclangmm/Utility.h"
|
2017-05-10 04:00:05 +00:00
|
|
|
#include "platform.h"
|
2017-03-26 21:40:34 +00:00
|
|
|
#include "timer.h"
|
|
|
|
|
|
|
|
#include <algorithm>
|
2017-04-17 01:22:59 +00:00
|
|
|
#include <thread>
|
2017-03-26 21:40:34 +00:00
|
|
|
|
2017-06-29 02:50:30 +00:00
|
|
|
/*
|
|
|
|
#include <execinfo.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
2017-03-26 21:40:34 +00:00
|
|
|
namespace {
|
2017-06-29 02:50:30 +00:00
|
|
|
|
2017-06-29 17:30:22 +00:00
|
|
|
#if false
|
2017-06-29 02:50:30 +00:00
|
|
|
constexpr int kBacktraceBufferSize = 300;
|
|
|
|
|
|
|
|
void EmitBacktrace() {
|
|
|
|
void* buffer[kBacktraceBufferSize];
|
|
|
|
int nptrs = backtrace(buffer, kBacktraceBufferSize);
|
|
|
|
|
|
|
|
fprintf(stderr, "backtrace() returned %d addresses\n", nptrs);
|
|
|
|
|
|
|
|
/* The call backtrace_symbols_fd(buffer, nptrs, STDOUT_FILENO)
|
|
|
|
would produce similar output to the following: */
|
|
|
|
|
|
|
|
char** strings = backtrace_symbols(buffer, nptrs);
|
|
|
|
if (!strings) {
|
|
|
|
perror("Failed to emit backtrace");
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int j = 0; j < nptrs; j++)
|
|
|
|
fprintf(stderr, "%s\n", strings[j]);
|
|
|
|
|
|
|
|
free(strings);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2017-03-26 21:40:34 +00:00
|
|
|
unsigned Flags() {
|
2017-05-25 02:04:19 +00:00
|
|
|
// TODO: use clang_defaultEditingTranslationUnitOptions()?
|
2017-03-27 04:04:48 +00:00
|
|
|
return
|
2017-05-10 04:52:15 +00:00
|
|
|
CXTranslationUnit_Incomplete |
|
|
|
|
CXTranslationUnit_KeepGoing |
|
2017-03-27 04:04:48 +00:00
|
|
|
CXTranslationUnit_CacheCompletionResults |
|
|
|
|
CXTranslationUnit_PrecompiledPreamble |
|
2017-05-10 04:52:15 +00:00
|
|
|
CXTranslationUnit_IncludeBriefCommentsInCodeCompletion
|
2017-05-09 05:15:35 +00:00
|
|
|
#if !defined(_WIN32)
|
|
|
|
// For whatever reason, CreatePreambleOnFirstParse causes clang to become
|
|
|
|
// very crashy on windows.
|
|
|
|
// TODO: do more investigation, submit fixes to clang.
|
2017-05-10 04:52:15 +00:00
|
|
|
| CXTranslationUnit_CreatePreambleOnFirstParse
|
2017-05-09 05:15:35 +00:00
|
|
|
#endif
|
2017-05-10 04:52:15 +00:00
|
|
|
;
|
|
|
|
}
|
|
|
|
|
|
|
|
int GetCompletionPriority(const CXCompletionString& str, CXCursorKind result_kind, const std::string& label) {
|
|
|
|
int priority = clang_getCompletionPriority(str);
|
|
|
|
if (result_kind == CXCursor_Destructor) {
|
|
|
|
priority *= 100;
|
|
|
|
//std::cerr << "Bumping[destructor] " << ls_completion_item.label << std::endl;
|
|
|
|
}
|
|
|
|
if (result_kind == CXCursor_ConversionFunction ||
|
|
|
|
(result_kind == CXCursor_CXXMethod && StartsWith(label, "operator"))) {
|
|
|
|
//std::cerr << "Bumping[conversion] " << ls_completion_item.label << std::endl;
|
|
|
|
priority *= 100;
|
|
|
|
}
|
|
|
|
if (clang_getCompletionAvailability(str) != CXAvailability_Available) {
|
|
|
|
//std::cerr << "Bumping[notavailable] " << ls_completion_item.label << std::endl;
|
|
|
|
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-05-15 07:28:53 +00:00
|
|
|
case CXCursor_ObjCInstanceMethodDecl:
|
|
|
|
case CXCursor_CXXMethod:
|
|
|
|
return lsCompletionItemKind::Method;
|
|
|
|
|
|
|
|
case CXCursor_FunctionTemplate:
|
|
|
|
case CXCursor_FunctionDecl:
|
|
|
|
return lsCompletionItemKind::Function;
|
|
|
|
|
|
|
|
case CXCursor_Constructor:
|
|
|
|
case CXCursor_Destructor:
|
|
|
|
case CXCursor_ConversionFunction:
|
|
|
|
return lsCompletionItemKind::Constructor;
|
|
|
|
|
|
|
|
case CXCursor_FieldDecl:
|
|
|
|
return lsCompletionItemKind::Field;
|
|
|
|
|
|
|
|
case CXCursor_VarDecl:
|
|
|
|
case CXCursor_ParmDecl:
|
|
|
|
return lsCompletionItemKind::Variable;
|
|
|
|
|
|
|
|
case CXCursor_UnionDecl:
|
|
|
|
case CXCursor_ClassTemplate:
|
|
|
|
case CXCursor_ClassTemplatePartialSpecialization:
|
|
|
|
case CXCursor_ClassDecl:
|
|
|
|
case CXCursor_StructDecl:
|
|
|
|
case CXCursor_UsingDeclaration:
|
|
|
|
case CXCursor_TypedefDecl:
|
|
|
|
case CXCursor_TypeAliasDecl:
|
|
|
|
case CXCursor_TypeAliasTemplateDecl:
|
|
|
|
return lsCompletionItemKind::Class;
|
|
|
|
|
|
|
|
case CXCursor_EnumConstantDecl:
|
|
|
|
case CXCursor_EnumDecl:
|
|
|
|
return lsCompletionItemKind::Enum;
|
|
|
|
|
|
|
|
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:
|
|
|
|
return lsCompletionItemKind::Reference;
|
|
|
|
|
|
|
|
//return lsCompletionItemKind::Property;
|
|
|
|
//return lsCompletionItemKind::Unit;
|
|
|
|
//return lsCompletionItemKind::Value;
|
|
|
|
//return lsCompletionItemKind::Keyword;
|
|
|
|
//return lsCompletionItemKind::Snippet;
|
|
|
|
//return lsCompletionItemKind::Color;
|
|
|
|
//return lsCompletionItemKind::File;
|
|
|
|
|
|
|
|
case CXCursor_NotImplemented:
|
|
|
|
return lsCompletionItemKind::Text;
|
|
|
|
|
|
|
|
default:
|
|
|
|
std::cerr << "[complete] Unhandled completion kind " << cursor_kind << std::endl;
|
|
|
|
return lsCompletionItemKind::Text;
|
2017-03-26 21:40:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-05-15 07:58:29 +00:00
|
|
|
void BuildDetailString(CXCompletionString completion_string, std::string& label, std::string& detail, std::string& insert, std::vector<std::string>* parameters) {
|
2017-03-26 21:40:34 +00:00
|
|
|
int num_chunks = clang_getNumCompletionChunks(completion_string);
|
2017-04-16 08:55:14 +00:00
|
|
|
for (int i = 0; i < num_chunks; ++i) {
|
2017-03-26 21:40:34 +00:00
|
|
|
CXCompletionChunkKind kind = clang_getCompletionChunkKind(completion_string, i);
|
|
|
|
|
|
|
|
switch (kind) {
|
|
|
|
case CXCompletionChunk_Optional: {
|
|
|
|
CXCompletionString nested = clang_getCompletionChunkCompletionString(completion_string, i);
|
2017-05-15 07:58:29 +00:00
|
|
|
BuildDetailString(nested, label, detail, insert, parameters);
|
2017-03-26 21:40:34 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case CXCompletionChunk_Placeholder: {
|
2017-05-15 07:28:53 +00:00
|
|
|
std::string text = clang::ToString(clang_getCompletionChunkText(completion_string, i));
|
|
|
|
parameters->push_back(text);
|
|
|
|
detail += text;
|
|
|
|
insert += "${" + std::to_string(parameters->size()) + ":" + text + "}";
|
2017-03-26 21:40:34 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2017-05-15 07:28:53 +00:00
|
|
|
case CXCompletionChunk_CurrentParameter:
|
|
|
|
// We have our own parsing logic for active parameter. This doesn't seem
|
|
|
|
// to be very reliable.
|
|
|
|
break;
|
|
|
|
|
2017-05-15 07:58:29 +00:00
|
|
|
case CXCompletionChunk_TypedText: {
|
2017-05-15 07:28:53 +00:00
|
|
|
std::string text = clang::ToString(clang_getCompletionChunkText(completion_string, i));
|
2017-05-15 07:58:29 +00:00
|
|
|
label = text;
|
2017-05-15 07:28:53 +00:00
|
|
|
detail += text;
|
|
|
|
insert += text;
|
2017-03-26 21:40:34 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2017-05-15 07:58:29 +00:00
|
|
|
case CXCompletionChunk_Text: {
|
|
|
|
std::string text = clang::ToString(clang_getCompletionChunkText(completion_string, i));
|
|
|
|
detail += text;
|
|
|
|
insert += text;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case CXCompletionChunk_Informative: {
|
|
|
|
detail += clang::ToString(clang_getCompletionChunkText(completion_string, i));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2017-03-26 21:40:34 +00:00
|
|
|
case CXCompletionChunk_ResultType: {
|
|
|
|
CXString text = clang_getCompletionChunkText(completion_string, i);
|
|
|
|
std::string new_detail = clang::ToString(text) + detail + " ";
|
|
|
|
detail = new_detail;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case CXCompletionChunk_LeftParen:
|
|
|
|
detail += "(";
|
2017-05-15 07:28:53 +00:00
|
|
|
insert += "(";
|
2017-03-26 21:40:34 +00:00
|
|
|
break;
|
|
|
|
case CXCompletionChunk_RightParen:
|
|
|
|
detail += ")";
|
2017-05-15 07:28:53 +00:00
|
|
|
insert += ")";
|
2017-03-26 21:40:34 +00:00
|
|
|
break;
|
|
|
|
case CXCompletionChunk_LeftBracket:
|
2017-05-15 07:28:53 +00:00
|
|
|
detail += "[";
|
|
|
|
insert += "[";
|
2017-03-26 21:40:34 +00:00
|
|
|
break;
|
|
|
|
case CXCompletionChunk_RightBracket:
|
2017-05-15 07:28:53 +00:00
|
|
|
detail += "]";
|
|
|
|
insert += "]";
|
2017-03-26 21:40:34 +00:00
|
|
|
break;
|
|
|
|
case CXCompletionChunk_LeftBrace:
|
|
|
|
detail += "{";
|
2017-05-15 07:28:53 +00:00
|
|
|
insert += "{";
|
2017-03-26 21:40:34 +00:00
|
|
|
break;
|
|
|
|
case CXCompletionChunk_RightBrace:
|
|
|
|
detail += "}";
|
2017-05-15 07:28:53 +00:00
|
|
|
insert += "}";
|
2017-03-26 21:40:34 +00:00
|
|
|
break;
|
|
|
|
case CXCompletionChunk_LeftAngle:
|
|
|
|
detail += "<";
|
2017-05-15 07:28:53 +00:00
|
|
|
insert += "<";
|
2017-03-26 21:40:34 +00:00
|
|
|
break;
|
|
|
|
case CXCompletionChunk_RightAngle:
|
|
|
|
detail += ">";
|
2017-05-15 07:28:53 +00:00
|
|
|
insert += ">";
|
2017-03-26 21:40:34 +00:00
|
|
|
break;
|
|
|
|
case CXCompletionChunk_Comma:
|
|
|
|
detail += ", ";
|
2017-05-15 07:28:53 +00:00
|
|
|
insert += ", ";
|
2017-03-26 21:40:34 +00:00
|
|
|
break;
|
|
|
|
case CXCompletionChunk_Colon:
|
|
|
|
detail += ":";
|
2017-05-15 07:28:53 +00:00
|
|
|
insert += ":";
|
2017-03-26 21:40:34 +00:00
|
|
|
break;
|
|
|
|
case CXCompletionChunk_SemiColon:
|
|
|
|
detail += ";";
|
2017-05-15 07:28:53 +00:00
|
|
|
insert += ";";
|
2017-03-26 21:40:34 +00:00
|
|
|
break;
|
|
|
|
case CXCompletionChunk_Equal:
|
|
|
|
detail += "=";
|
2017-05-15 07:28:53 +00:00
|
|
|
insert += "=";
|
2017-03-26 21:40:34 +00:00
|
|
|
break;
|
|
|
|
case CXCompletionChunk_HorizontalSpace:
|
|
|
|
case CXCompletionChunk_VerticalSpace:
|
|
|
|
detail += " ";
|
2017-05-15 07:28:53 +00:00
|
|
|
insert += " ";
|
2017-03-26 21:40:34 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-04-17 01:22:59 +00:00
|
|
|
|
2017-06-10 04:13:16 +00:00
|
|
|
void EnsureDocumentParsed(ClangCompleteManager* manager,
|
2017-06-10 04:15:33 +00:00
|
|
|
std::shared_ptr<CompletionSession> session,
|
2017-05-11 06:25:41 +00:00
|
|
|
std::unique_ptr<clang::TranslationUnit>* tu,
|
2017-05-26 06:40:38 +00:00
|
|
|
clang::Index* index) {
|
|
|
|
// 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;
|
|
|
|
args.push_back("-fparse-all-comments");
|
|
|
|
|
|
|
|
std::vector<CXUnsavedFile> unsaved = session->working_files->AsUnsavedFiles();
|
|
|
|
|
|
|
|
std::cerr << "[complete] Creating completion session with arguments " << StringJoin(args) << std::endl;
|
2017-05-26 06:40:38 +00:00
|
|
|
*tu = MakeUnique<clang::TranslationUnit>(index, session->file.filename, args, unsaved, Flags());
|
2017-05-11 06:25:41 +00:00
|
|
|
std::cerr << "[complete] Done creating active; did_fail=" << (*tu)->did_fail << std::endl;
|
2017-06-10 04:13:16 +00:00
|
|
|
|
|
|
|
// Build diagnostics.
|
2017-07-16 00:25:52 +00:00
|
|
|
if (manager->config_->diagnosticsOnParse && !(*tu)->did_fail) {
|
2017-06-10 04:13:16 +00:00
|
|
|
NonElidedVector<lsDiagnostic> ls_diagnostics;
|
|
|
|
unsigned num_diagnostics = clang_getNumDiagnostics((*tu)->cx_tu);
|
|
|
|
for (unsigned i = 0; i < num_diagnostics; ++i) {
|
2017-07-12 22:02:48 +00:00
|
|
|
optional<lsDiagnostic> diagnostic = BuildAndDisposeDiagnostic(
|
|
|
|
clang_getDiagnostic((*tu)->cx_tu, i), session->file.filename);
|
2017-06-10 04:13:16 +00:00
|
|
|
if (diagnostic)
|
|
|
|
ls_diagnostics.push_back(*diagnostic);
|
|
|
|
}
|
|
|
|
manager->on_diagnostic_(session->file.filename, ls_diagnostics);
|
|
|
|
}
|
2017-05-11 06:25:41 +00:00
|
|
|
}
|
|
|
|
|
2017-05-27 04:21:00 +00:00
|
|
|
void CompletionParseMain(ClangCompleteManager* completion_manager) {
|
2017-05-11 06:25:41 +00:00
|
|
|
while (true) {
|
|
|
|
// Fetching the completion request blocks until we have a request.
|
2017-05-27 04:21:00 +00:00
|
|
|
ClangCompleteManager::ParseRequest request = completion_manager->parse_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-06-10 04:15:33 +00:00
|
|
|
std::shared_ptr<CompletionSession> session = completion_manager->TryGetSession(request.path, false /*create_if_needed*/);
|
2017-05-26 06:40:38 +00:00
|
|
|
if (!session)
|
|
|
|
continue;
|
2017-05-11 06:25:41 +00:00
|
|
|
|
2017-05-26 06:40:38 +00:00
|
|
|
// If we've parsed it more recently than the request time, don't bother
|
|
|
|
// reparsing.
|
|
|
|
if (session->tu_last_parsed_at &&
|
|
|
|
*session->tu_last_parsed_at > request.request_time) {
|
|
|
|
continue;
|
|
|
|
}
|
2017-05-11 06:25:41 +00:00
|
|
|
|
2017-05-26 06:40:38 +00:00
|
|
|
std::unique_ptr<clang::TranslationUnit> parsing;
|
2017-06-10 04:13:16 +00:00
|
|
|
EnsureDocumentParsed(completion_manager, session, &parsing, &session->index);
|
2017-05-26 06:40:38 +00:00
|
|
|
|
|
|
|
// Activate new translation unit.
|
|
|
|
// tu_last_parsed_at is only read by this thread, so it doesn't need to be under the mutex.
|
|
|
|
session->tu_last_parsed_at = std::chrono::high_resolution_clock::now();
|
|
|
|
std::lock_guard<std::mutex> lock(session->tu_lock);
|
2017-07-17 17:41:59 +00:00
|
|
|
std::cerr << "[completion] Swapping completion session for " << request.path << std::endl;
|
2017-05-26 06:40:38 +00:00
|
|
|
session->tu = std::move(parsing);
|
2017-05-11 06:25:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-07-16 00:08:07 +00:00
|
|
|
void CompletionDiagnosticsDelayedRefreshMain(ClangCompleteManager* completion_manager) {
|
|
|
|
constexpr int kSecondsToWaitForDiagnosticsRefresh = 5;
|
|
|
|
|
|
|
|
// Refreshes diagnostics a few seconds after the final code completion, since
|
|
|
|
// we don't get a language server request.
|
|
|
|
while (true) {
|
|
|
|
std::unique_lock<std::mutex> l(completion_manager->delayed_diagnostic_wakeup_mtx_);
|
|
|
|
completion_manager->delayed_diagnostic_wakeup_cv_.wait(l);
|
|
|
|
|
|
|
|
// Check for spurious wakeup.
|
|
|
|
if (!completion_manager->delayed_diagnostic_last_completion_position_)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
while (true) {
|
|
|
|
// Get completion request info.
|
|
|
|
if (!l.owns_lock())
|
|
|
|
l.lock();
|
|
|
|
lsTextDocumentPositionParams location = *completion_manager->delayed_diagnostic_last_completion_position_;
|
|
|
|
completion_manager->delayed_diagnostic_last_completion_position_.reset();
|
|
|
|
l.unlock();
|
|
|
|
|
|
|
|
// Wait five seconds. If there was another completion request, start the
|
|
|
|
// waiting process over again.
|
|
|
|
std::this_thread::sleep_for(std::chrono::seconds(kSecondsToWaitForDiagnosticsRefresh));
|
|
|
|
l.lock();
|
|
|
|
bool has_completion_since_sleeping = completion_manager->delayed_diagnostic_last_completion_position_.has_value();
|
|
|
|
l.unlock();
|
|
|
|
if (has_completion_since_sleeping)
|
|
|
|
continue;
|
|
|
|
|
2017-07-19 07:20:19 +00:00
|
|
|
// Push completion request to the end of the line. clang may report errors
|
|
|
|
// if completing in the middle of the line. Not a perfect hueristic, but
|
|
|
|
// probably good enough.
|
|
|
|
// TODO: Consider scanning for a semicolon if this doesn't work well in
|
|
|
|
// practice.
|
|
|
|
location.position.character = 10000;
|
|
|
|
|
2017-07-16 00:08:07 +00:00
|
|
|
// Make completion request to get refreshed diagnostics.
|
|
|
|
auto request = MakeUnique<ClangCompleteManager::CompletionRequest>();
|
|
|
|
request->location = location;
|
|
|
|
completion_manager->completion_request_.SetIfEmpty(std::move(request));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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-05-27 04:21:00 +00:00
|
|
|
std::unique_ptr<ClangCompleteManager::CompletionRequest> request = completion_manager->completion_request_.Take();
|
2017-05-26 06:40:38 +00:00
|
|
|
std::string path = request->location.textDocument.uri.GetPath();
|
2017-04-17 01:22:59 +00:00
|
|
|
|
2017-06-10 04:15:33 +00:00
|
|
|
std::shared_ptr<CompletionSession> session = completion_manager->TryGetSession(path, true /*create_if_needed*/);
|
2017-06-10 01:02:48 +00:00
|
|
|
|
2017-05-26 06:40:38 +00:00
|
|
|
std::lock_guard<std::mutex> lock(session->tu_lock);
|
2017-06-10 04:13:16 +00:00
|
|
|
EnsureDocumentParsed(completion_manager, session, &session->tu, &session->index);
|
2017-04-17 01:22:59 +00:00
|
|
|
|
2017-05-26 06:40:38 +00:00
|
|
|
// Language server is 0-based, clang is 1-based.
|
2017-04-17 01:22:59 +00:00
|
|
|
unsigned line = request->location.position.line + 1;
|
|
|
|
unsigned column = request->location.position.character + 1;
|
|
|
|
|
2017-05-10 04:52:15 +00:00
|
|
|
std::cerr << "[complete] Completing at " << line << ":" << column << std::endl;
|
|
|
|
|
|
|
|
Timer timer;
|
2017-04-17 01:22:59 +00:00
|
|
|
|
2017-05-26 06:40:38 +00:00
|
|
|
std::vector<CXUnsavedFile> unsaved = completion_manager->working_files_->AsUnsavedFiles();
|
2017-05-10 04:52:15 +00:00
|
|
|
timer.ResetAndPrint("[complete] Fetching unsaved files");
|
2017-04-17 01:22:59 +00:00
|
|
|
|
|
|
|
timer.Reset();
|
2017-05-21 23:22:00 +00:00
|
|
|
unsigned const kCompleteOptions = CXCodeComplete_IncludeMacros | CXCodeComplete_IncludeBriefComments;
|
2017-04-17 01:22:59 +00:00
|
|
|
CXCodeCompleteResults* cx_results = clang_codeCompleteAt(
|
2017-05-26 06:40:38 +00:00
|
|
|
session->tu->cx_tu,
|
2017-04-17 01:22:59 +00:00
|
|
|
session->file.filename.c_str(), line, column,
|
2017-05-21 23:22:00 +00:00
|
|
|
unsaved.data(), (unsigned)unsaved.size(),
|
|
|
|
kCompleteOptions);
|
2017-04-17 01:22:59 +00:00
|
|
|
if (!cx_results) {
|
2017-05-26 06:40:38 +00:00
|
|
|
timer.ResetAndPrint("[complete] Code completion failed");
|
2017-07-17 20:03:42 +00:00
|
|
|
if (request->on_complete)
|
|
|
|
request->on_complete({}, false /*is_cached_result*/);
|
2017-04-17 01:22:59 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2017-05-10 04:52:15 +00:00
|
|
|
timer.ResetAndPrint("[complete] clangCodeCompleteAt");
|
|
|
|
std::cerr << "[complete] Got " << cx_results->NumResults << " results" << std::endl;
|
2017-06-10 01:02:48 +00:00
|
|
|
|
2017-06-29 02:50:30 +00:00
|
|
|
{
|
2017-07-16 00:08:07 +00:00
|
|
|
if (request->on_complete) {
|
|
|
|
NonElidedVector<lsCompletionItem> ls_result;
|
|
|
|
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)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// TODO: fill in more data
|
|
|
|
lsCompletionItem ls_completion_item;
|
|
|
|
|
|
|
|
// kind/label/detail/docs/sortText
|
|
|
|
ls_completion_item.kind = GetCompletionKind(result.CursorKind);
|
|
|
|
BuildDetailString(result.CompletionString, ls_completion_item.label, ls_completion_item.detail, ls_completion_item.insertText, &ls_completion_item.parameters_);
|
|
|
|
ls_completion_item.insertText += "$0";
|
|
|
|
ls_completion_item.documentation = clang::ToString(clang_getCompletionBriefComment(result.CompletionString));
|
|
|
|
ls_completion_item.sortText = (const char)uint64_t(GetCompletionPriority(result.CompletionString, result.CursorKind, ls_completion_item.label));
|
|
|
|
|
|
|
|
ls_result.push_back(ls_completion_item);
|
|
|
|
}
|
|
|
|
timer.ResetAndPrint("[complete] Building " + std::to_string(ls_result.size()) + " completion results");
|
|
|
|
|
|
|
|
request->on_complete(ls_result, false /*is_cached_result*/);
|
|
|
|
timer.ResetAndPrint("[complete] Running user-given completion func");
|
2017-06-29 02:50:30 +00:00
|
|
|
}
|
2017-07-12 18:25:58 +00:00
|
|
|
|
2017-07-16 00:25:52 +00:00
|
|
|
if (completion_manager->config_->diagnosticsOnCodeCompletion) {
|
|
|
|
unsigned num_diagnostics = clang_codeCompleteGetNumDiagnostics(cx_results);
|
|
|
|
NonElidedVector<lsDiagnostic> ls_diagnostics;
|
|
|
|
for (unsigned i = 0; i < num_diagnostics; ++i) {
|
|
|
|
CXDiagnostic cx_diag = clang_codeCompleteGetDiagnostic(cx_results, i);
|
|
|
|
optional<lsDiagnostic> diagnostic = BuildAndDisposeDiagnostic(cx_diag, path);
|
|
|
|
if (diagnostic)
|
|
|
|
ls_diagnostics.push_back(*diagnostic);
|
|
|
|
}
|
|
|
|
completion_manager->on_diagnostic_(session->file.filename, ls_diagnostics);
|
|
|
|
timer.ResetAndPrint("[complete] Build diagnostics");
|
2017-07-12 18:25:58 +00:00
|
|
|
}
|
2017-04-17 01:22:59 +00:00
|
|
|
}
|
|
|
|
|
2017-06-29 02:50:30 +00:00
|
|
|
// Make sure |ls_results| is destroyed before clearing |cx_results|.
|
2017-04-17 01:22:59 +00:00
|
|
|
clang_disposeCodeCompleteResults(cx_results);
|
2017-05-20 06:35:14 +00:00
|
|
|
timer.ResetAndPrint("[complete] clang_disposeCodeCompleteResults");
|
|
|
|
|
2017-07-16 00:25:52 +00:00
|
|
|
if (completion_manager->config_->diagnosticsOnCodeCompletion &&
|
|
|
|
request->is_user_completion) {
|
2017-07-16 00:08:07 +00:00
|
|
|
{
|
|
|
|
std::lock_guard<std::mutex> lock(completion_manager->delayed_diagnostic_wakeup_mtx_);
|
|
|
|
completion_manager->delayed_diagnostic_last_completion_position_ = request->location;
|
|
|
|
}
|
|
|
|
completion_manager->delayed_diagnostic_wakeup_cv_.notify_one();
|
|
|
|
}
|
|
|
|
|
2017-04-17 01:22:59 +00:00
|
|
|
continue;
|
|
|
|
}
|
2017-03-26 21:40:34 +00:00
|
|
|
}
|
|
|
|
|
2017-04-17 01:22:59 +00:00
|
|
|
} // namespace
|
|
|
|
|
2017-05-10 04:52:15 +00:00
|
|
|
CompletionSession::CompletionSession(const Project::Entry& file, WorkingFiles* working_files)
|
2017-06-14 06:15:18 +00:00
|
|
|
: file(file), working_files(working_files), index(0 /*excludeDeclarationsFromPCH*/, 0 /*displayDiagnostics*/) {
|
|
|
|
std::cerr << "[complete] CompletionSession::CompletionSession() for " << file.filename << std::endl;
|
|
|
|
}
|
2017-05-10 04:52:15 +00:00
|
|
|
|
2017-06-14 06:15:18 +00:00
|
|
|
CompletionSession::~CompletionSession() {
|
|
|
|
std::cerr << "[complete] CompletionSession::~CompletionSession() for " << file.filename << std::endl;
|
2017-06-29 02:50:30 +00:00
|
|
|
// EmitBacktrace();
|
2017-06-14 06:15:18 +00:00
|
|
|
}
|
2017-05-10 04:52:15 +00:00
|
|
|
|
2017-05-26 06:40:38 +00:00
|
|
|
LruSessionCache::LruSessionCache(int max_entries) : max_entries_(max_entries) {}
|
|
|
|
|
2017-06-10 04:15:33 +00:00
|
|
|
std::shared_ptr<CompletionSession> LruSessionCache::TryGetEntry(const std::string& filename) {
|
2017-05-26 06:40:38 +00:00
|
|
|
for (int i = 0; i < entries_.size(); ++i) {
|
|
|
|
if (entries_[i]->file.filename == filename)
|
2017-06-10 04:15:33 +00:00
|
|
|
return entries_[i];
|
2017-05-26 06:40:38 +00:00
|
|
|
}
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2017-06-10 04:13:16 +00:00
|
|
|
std::shared_ptr<CompletionSession> LruSessionCache::TryTakeEntry(const std::string& filename) {
|
2017-05-27 04:21:00 +00:00
|
|
|
for (int i = 0; i < entries_.size(); ++i) {
|
|
|
|
if (entries_[i]->file.filename == filename) {
|
2017-06-29 02:50:30 +00:00
|
|
|
std::shared_ptr<CompletionSession> result = entries_[i];
|
2017-05-27 04:21:00 +00:00
|
|
|
entries_.erase(entries_.begin() + i);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2017-06-10 04:13:16 +00:00
|
|
|
void LruSessionCache::InsertEntry(std::shared_ptr<CompletionSession> session) {
|
2017-06-29 02:50:30 +00:00
|
|
|
if (entries_.size() && entries_.size() >= max_entries_)
|
2017-05-26 06:40:38 +00:00
|
|
|
entries_.pop_back();
|
2017-06-29 02:50:30 +00:00
|
|
|
entries_.insert(entries_.begin(), session);
|
2017-05-26 06:40:38 +00:00
|
|
|
}
|
|
|
|
|
2017-05-27 04:21:00 +00:00
|
|
|
ClangCompleteManager::ParseRequest::ParseRequest(const std::string& path)
|
2017-06-10 01:02:48 +00:00
|
|
|
: request_time(std::chrono::high_resolution_clock::now()), path(path) {}
|
2017-05-26 06:40:38 +00:00
|
|
|
|
2017-06-10 04:13:16 +00:00
|
|
|
ClangCompleteManager::ClangCompleteManager(Config* config, Project* project, WorkingFiles* working_files, OnDiagnostic on_diagnostic)
|
|
|
|
: config_(config), project_(project), working_files_(working_files), on_diagnostic_(on_diagnostic),
|
2017-05-26 06:40:38 +00:00
|
|
|
view_sessions_(kMaxViewSessions), edit_sessions_(kMaxEditSessions) {
|
2017-04-17 01:22:59 +00:00
|
|
|
new std::thread([&]() {
|
2017-05-11 06:25:41 +00:00
|
|
|
SetCurrentThreadName("completequery");
|
|
|
|
CompletionQueryMain(this);
|
2017-07-16 00:08:07 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
new std::thread([&]() {
|
|
|
|
SetCurrentThreadName("completediagnosticsrefresh");
|
|
|
|
CompletionDiagnosticsDelayedRefreshMain(this);
|
2017-05-11 06:25:41 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
new std::thread([&]() {
|
|
|
|
SetCurrentThreadName("completeparse");
|
|
|
|
CompletionParseMain(this);
|
2017-04-17 01:22:59 +00:00
|
|
|
});
|
|
|
|
}
|
2017-03-26 21:40:34 +00:00
|
|
|
|
2017-06-29 02:50:30 +00:00
|
|
|
ClangCompleteManager::~ClangCompleteManager() {}
|
|
|
|
|
2017-05-27 04:21:00 +00:00
|
|
|
void ClangCompleteManager::CodeComplete(const lsTextDocumentPositionParams& completion_location, const OnComplete& on_complete) {
|
2017-05-26 06:40:38 +00:00
|
|
|
// completion thread will create the CompletionSession if needed.
|
|
|
|
|
2017-04-17 01:22:59 +00:00
|
|
|
auto request = MakeUnique<CompletionRequest>();
|
|
|
|
request->location = completion_location;
|
|
|
|
request->on_complete = on_complete;
|
2017-07-16 00:08:07 +00:00
|
|
|
request->is_user_completion = true;
|
2017-05-26 06:40:38 +00:00
|
|
|
completion_request_.Set(std::move(request));
|
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-05-26 06:40:38 +00:00
|
|
|
std::lock_guard<std::mutex> lock(sessions_lock_);
|
|
|
|
|
|
|
|
if (view_sessions_.TryGetEntry(filename))
|
|
|
|
return;
|
|
|
|
|
|
|
|
std::cerr << "[complete] Creating new edit code completion session for " << filename << std::endl;
|
2017-06-29 02:50:30 +00:00
|
|
|
view_sessions_.InsertEntry(std::make_shared<CompletionSession>(
|
2017-05-26 06:40:38 +00:00
|
|
|
project_->FindCompilationEntryForFile(filename), working_files_));
|
|
|
|
parse_requests_.Enqueue(ParseRequest(filename));
|
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
|
|
|
//
|
|
|
|
// On edit, 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-05-10 04:52:15 +00:00
|
|
|
|
2017-05-26 06:40:38 +00:00
|
|
|
std::lock_guard<std::mutex> lock(sessions_lock_);
|
|
|
|
|
|
|
|
if (edit_sessions_.TryGetEntry(filename))
|
|
|
|
return;
|
|
|
|
|
2017-06-29 02:50:30 +00:00
|
|
|
std::shared_ptr<CompletionSession> session = view_sessions_.TryTakeEntry(filename);
|
|
|
|
if (session) {
|
|
|
|
edit_sessions_.InsertEntry(session);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
std::cerr << "[complete] Creating new edit code completion session for " << filename << std::endl;
|
|
|
|
edit_sessions_.InsertEntry(std::make_shared<CompletionSession>(
|
|
|
|
project_->FindCompilationEntryForFile(filename), working_files_));
|
|
|
|
parse_requests_.PriorityEnqueue(ParseRequest(filename));
|
2017-05-27 04:21:00 +00:00
|
|
|
}
|
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.
|
|
|
|
//
|
|
|
|
|
|
|
|
std::lock_guard<std::mutex> lock(sessions_lock_);
|
|
|
|
|
|
|
|
if (!edit_sessions_.TryGetEntry(filename)) {
|
|
|
|
std::cerr << "[complete] Creating new edit code completion session for " << filename << std::endl;
|
2017-06-29 02:50:30 +00:00
|
|
|
edit_sessions_.InsertEntry(std::make_shared<CompletionSession>(
|
2017-05-26 06:40:38 +00:00
|
|
|
project_->FindCompilationEntryForFile(filename), working_files_));
|
2017-05-10 04:52:15 +00:00
|
|
|
}
|
2017-05-11 06:25:41 +00:00
|
|
|
|
2017-05-26 06:40:38 +00:00
|
|
|
parse_requests_.PriorityEnqueue(ParseRequest(filename));
|
|
|
|
}
|
|
|
|
|
2017-06-10 04:15:33 +00:00
|
|
|
std::shared_ptr<CompletionSession> ClangCompleteManager::TryGetSession(const std::string& filename, bool create_if_needed) {
|
2017-05-26 06:40:38 +00:00
|
|
|
std::lock_guard<std::mutex> lock(sessions_lock_);
|
|
|
|
|
2017-06-10 04:15:33 +00:00
|
|
|
std::shared_ptr<CompletionSession> session = edit_sessions_.TryGetEntry(filename);
|
2017-06-10 01:02:48 +00:00
|
|
|
|
2017-05-26 06:40:38 +00:00
|
|
|
if (!session)
|
|
|
|
session = view_sessions_.TryGetEntry(filename);
|
2017-06-10 01:02:48 +00:00
|
|
|
|
2017-05-26 06:40:38 +00:00
|
|
|
if (!session && create_if_needed) {
|
|
|
|
// Create new session. Default to edited_sessions_ since invoking code
|
|
|
|
// completion almost certainly implies an edit.
|
2017-06-29 02:50:30 +00:00
|
|
|
edit_sessions_.InsertEntry(std::make_shared<CompletionSession>(
|
2017-05-26 06:40:38 +00:00
|
|
|
project_->FindCompilationEntryForFile(filename), working_files_));
|
|
|
|
session = edit_sessions_.TryGetEntry(filename);
|
|
|
|
}
|
2017-06-10 01:02:48 +00:00
|
|
|
|
2017-05-26 06:40:38 +00:00
|
|
|
return session;
|
2017-06-10 01:02:48 +00:00
|
|
|
}
|