diff --git a/src/clang_complete.cc b/src/clang_complete.cc index ffaa286d..5ccad32c 100644 --- a/src/clang_complete.cc +++ b/src/clang_complete.cc @@ -143,13 +143,6 @@ struct PreambleData { }; namespace { - -std::string StripFileType(const std::string &path) { - SmallString<128> Ret; - sys::path::append(Ret, sys::path::parent_path(path), sys::path::stem(path)); - return sys::path::convert_to_slash(Ret); -} - bool LocationInRange(SourceLocation L, CharSourceRange R, const SourceManager &M) { assert(R.isCharRange()); @@ -272,23 +265,16 @@ public: std::unique_ptr BuildCompilerInstance( CompletionSession &session, std::unique_ptr CI, IntrusiveRefCntPtr FS, DiagnosticConsumer &DC, - const PreambleData *preamble, const WorkingFiles::Snapshot &snapshot, - std::vector> &Bufs) { - std::string main = ResolveIfRelative( - session.file.directory, - sys::path::convert_to_slash(CI->getFrontendOpts().Inputs[0].getFile())); - for (auto &file : snapshot.files) { - Bufs.push_back(llvm::MemoryBuffer::getMemBuffer(file.content)); - if (preamble && file.filename == main) { + const PreambleData *preamble, const std::string &path, + std::unique_ptr &Buf) { + if (preamble) { #if LLVM_VERSION_MAJOR >= 7 - preamble->Preamble.OverridePreamble(*CI, FS, Bufs.back().get()); + preamble->Preamble.OverridePreamble(*CI, FS, Buf.get()); #else - preamble->Preamble.AddImplicitPreamble(*CI, FS, Bufs.back().get()); + preamble->Preamble.AddImplicitPreamble(*CI, FS, Buf.get()); #endif - continue; - } - CI->getPreprocessorOpts().addRemappedFile(file.filename, - Bufs.back().get()); + } else { + CI->getPreprocessorOpts().addRemappedFile(path, Buf.get()); } auto Clang = std::make_unique(session.PCH); @@ -358,8 +344,6 @@ void *CompletionPreloadMain(void *manager_) { continue; const auto &args = session->file.args; - WorkingFiles::Snapshot snapshot = - session->wfiles->AsSnapshot({StripFileType(session->file.filename)}); auto stat_cache = std::make_unique(); IntrusiveRefCntPtr FS = stat_cache->Producer(session->FS); @@ -415,19 +399,24 @@ void *CompletionMain(void *manager_) { CI->getLangOpts()->CommentOpts.ParseAllComments = true; DiagnosticConsumer DC; - WorkingFiles::Snapshot snapshot = - manager->wfiles_->AsSnapshot({StripFileType(path)}); - std::vector> Bufs; + std::string content = manager->wfiles_->GetContent(path); + auto Buf = llvm::MemoryBuffer::getMemBuffer(content); + bool in_preamble = + GetOffsetForPosition( + {request->position.line, request->position.character}, content) < + ComputePreambleBounds(*CI->getLangOpts(), Buf.get(), 0).Size; + if (in_preamble) + preamble.reset(); auto Clang = BuildCompilerInstance(*session, std::move(CI), FS, DC, - preamble.get(), snapshot, Bufs); + preamble.get(), path, Buf); if (!Clang) continue; + Clang->getPreprocessorOpts().SingleFileParseMode = in_preamble; Clang->setCodeCompletionConsumer(request->Consumer.release()); if (!Parse(*Clang)) continue; - for (auto &Buf : Bufs) - Buf.release(); + Buf.release(); request->on_complete(&Clang->getCodeCompletionConsumer()); } @@ -482,6 +471,7 @@ void *DiagnosticMain(void *manager_) { std::shared_ptr preamble = session->GetPreamble(); IntrusiveRefCntPtr FS = preamble ? preamble->stat_cache->Consumer(session->FS) : session->FS; + std::unique_ptr CI = BuildCompilerInvocation(session->file.args, FS); if (!CI) @@ -489,17 +479,15 @@ void *DiagnosticMain(void *manager_) { CI->getDiagnosticOpts().IgnoreWarnings = false; CI->getLangOpts()->SpellChecking = g_config->diagnostics.spellChecking; StoreDiags DC(path); - WorkingFiles::Snapshot snapshot = - manager->wfiles_->AsSnapshot({StripFileType(path)}); - std::vector> Bufs; + std::string content = manager->wfiles_->GetContent(path); + auto Buf = llvm::MemoryBuffer::getMemBuffer(content); auto Clang = BuildCompilerInstance(*session, std::move(CI), FS, DC, - preamble.get(), snapshot, Bufs); + preamble.get(), path, Buf); if (!Clang) continue; if (!Parse(*Clang)) continue; - for (auto &Buf : Bufs) - Buf.release(); + Buf.release(); auto Fill = [](const DiagBase &d, Diagnostic &ret) { ret.range = lsRange{{d.range.start.line, d.range.start.column}, diff --git a/src/include_complete.cc b/src/include_complete.cc index c806dcc8..fab2279b 100644 --- a/src/include_complete.cc +++ b/src/include_complete.cc @@ -104,7 +104,7 @@ IncludeComplete::IncludeComplete(Project *project) : is_scanning(false), project_(project) {} void IncludeComplete::Rescan() { - if (is_scanning) + if (is_scanning || LLVM_VERSION_MAJOR >= 8) return; completion_items.clear(); diff --git a/src/messages/textDocument_completion.cc b/src/messages/textDocument_completion.cc index 5c49f5a5..5748b8bd 100644 --- a/src/messages/textDocument_completion.cc +++ b/src/messages/textDocument_completion.cc @@ -24,7 +24,9 @@ limitations under the License. #include #include +#if LLVM_VERSION_MAJOR < 8 #include +#endif namespace ccls { using namespace clang; @@ -43,6 +45,7 @@ struct CompletionList { }; MAKE_REFLECT_STRUCT(CompletionList, isIncomplete, items); +#if LLVM_VERSION_MAJOR < 8 void DecorateIncludePaths(const std::smatch &match, std::vector *items) { std::string spaces_after_include = " "; @@ -90,6 +93,7 @@ ParseIncludeLineResult ParseIncludeLine(const std::string &line) { bool ok = std::regex_match(line, match, pattern); return {ok, match[3], match[5], match[6], match}; } +#endif // Pre-filters completion responses before sending to vscode. This results in a // significantly snappier completion experience as vscode is easily overloaded @@ -121,7 +125,7 @@ void FilterCandidates(CompletionList &result, const std::string &complete_text, for (auto &item : items) { item.textEdit.range = lsRange{begin_pos, end_pos}; if (has_open_paren && item.filterText) - item.textEdit.newText = item.filterText.value(); + item.textEdit.newText = *item.filterText; // https://github.com/Microsoft/language-server-protocol/issues/543 // Order of textEdit and additionalTextEdits is unspecified. auto &edits = item.additionalTextEdits; @@ -133,7 +137,7 @@ void FilterCandidates(CompletionList &result, const std::string &complete_text, item.filterText = buffer_line.substr(start.character, end.character - start.character) + - item.filterText.value(); + *item.filterText; } edits.erase(edits.begin()); } @@ -391,6 +395,10 @@ public: includeBriefComments()); CompletionItem ls_item; ls_item.kind = GetCompletionKind(R.CursorKind); +#if LLVM_VERSION_MAJOR >= 8 + if (Context.getKind() == CodeCompletionContext::CCC_IncludedFile) + ls_item.kind = CompletionItemKind::File; +#endif if (const char *brief = CCS->getBriefComment()) ls_item.documentation = brief; ls_item.detail = CCS->getParentContextName().str(); @@ -443,51 +451,44 @@ void MessageHandler::textDocument_completion(CompletionParam ¶m, param.position.line < file->buffer_lines.size()) buffer_line = file->buffer_lines[param.position.line]; - // Check for - and : before completing -> or ::, since vscode does not - // support multi-character trigger characters. + clang::CodeCompleteOptions CCOpts; + CCOpts.IncludeBriefComments = true; + CCOpts.IncludeCodePatterns = StringRef(buffer_line).ltrim().startswith("#"); +#if LLVM_VERSION_MAJOR >= 7 + CCOpts.IncludeFixIts = true; +#endif + CCOpts.IncludeMacros = true; + if (param.context.triggerKind == CompletionTriggerKind::TriggerCharacter && param.context.triggerCharacter) { - bool did_fail_check = false; - - std::string character = *param.context.triggerCharacter; - int preceding_index = param.position.character - 2; - - // If the character is '"', '<' or '/', make sure that the line starts - // with '#'. - if (character == "\"" || character == "<" || character == "/") { - size_t i = 0; - while (i < buffer_line.size() && isspace(buffer_line[i])) - ++i; - if (i >= buffer_line.size() || buffer_line[i] != '#') - did_fail_check = true; + bool ok = true; + int col = param.position.character - 2; + switch ((*param.context.triggerCharacter)[0]) { + case '"': + case '/': + case '<': + ok = CCOpts.IncludeCodePatterns; // start with # + break; + case ':': + ok = col >= 0 && buffer_line[col] == ':'; // :: + break; + case '>': + ok = col >= 0 && buffer_line[col] == '-'; // -> + break; } - // If the character is > or : and we are at the start of the line, do not - // show completion results. - else if ((character == ">" || character == ":") && preceding_index < 0) { - did_fail_check = true; - } - // If the character is > but - does not preced it, or if it is : and : - // does not preced it, do not show completion results. - else if (preceding_index >= 0 && - preceding_index < (int)buffer_line.size()) { - char preceding = buffer_line[preceding_index]; - did_fail_check = (preceding != '-' && character == ">") || - (preceding != ':' && character == ":"); - } - - if (did_fail_check) { + if (!ok) { reply(result); return; } } - std::string completion_text; + std::string filter; Position end_pos = param.position; - Position begin_pos = file->FindStableCompletionSource( - param.position, &completion_text, &end_pos); + Position begin_pos = + file->FindStableCompletionSource(param.position, &filter, &end_pos); +#if LLVM_VERSION_MAJOR < 8 ParseIncludeLineResult preprocess = ParseIncludeLine(buffer_line); - if (preprocess.ok && preprocess.keyword.compare("include") == 0) { CompletionList result; { @@ -506,47 +507,40 @@ void MessageHandler::textDocument_completion(CompletionParam ¶m, buffer_line); DecorateIncludePaths(preprocess.match, &result.items); reply(result); - } else { - std::string path = param.textDocument.uri.GetPath(); - CompletionManager::OnComplete callback = - [completion_text, path, begin_pos, end_pos, reply, - buffer_line](CodeCompleteConsumer *OptConsumer) { - if (!OptConsumer) - return; - auto *Consumer = static_cast(OptConsumer); - CompletionList result; - result.items = Consumer->ls_items; - - FilterCandidates(result, completion_text, begin_pos, end_pos, - buffer_line); - reply(result); - if (!Consumer->from_cache) { - cache.WithLock([&]() { - cache.path = path; - cache.position = begin_pos; - cache.result = Consumer->ls_items; - }); - } - }; - - clang::CodeCompleteOptions CCOpts; - CCOpts.IncludeBriefComments = true; - CCOpts.IncludeCodePatterns = preprocess.ok; // if there is a # -#if LLVM_VERSION_MAJOR >= 7 - CCOpts.IncludeFixIts = true; + return; + } #endif - CCOpts.IncludeMacros = true; - if (cache.IsCacheValid(path, begin_pos)) { - CompletionConsumer Consumer(CCOpts, true); - cache.WithLock([&]() { Consumer.ls_items = cache.result; }); - callback(&Consumer); - } else { - clang_complete->completion_request_.PushBack( - std::make_unique( - reply.id, param.textDocument, begin_pos, - std::make_unique(CCOpts, false), CCOpts, - callback)); - } + + CompletionManager::OnComplete callback = + [filter, path, begin_pos, end_pos, reply, + buffer_line](CodeCompleteConsumer *OptConsumer) { + if (!OptConsumer) + return; + auto *Consumer = static_cast(OptConsumer); + CompletionList result; + result.items = Consumer->ls_items; + + FilterCandidates(result, filter, begin_pos, end_pos, buffer_line); + reply(result); + if (!Consumer->from_cache) { + cache.WithLock([&]() { + cache.path = path; + cache.position = begin_pos; + cache.result = Consumer->ls_items; + }); + } + }; + + if (cache.IsCacheValid(path, begin_pos)) { + CompletionConsumer Consumer(CCOpts, true); + cache.WithLock([&]() { Consumer.ls_items = cache.result; }); + callback(&Consumer); + } else { + clang_complete->completion_request_.PushBack( + std::make_unique( + reply.id, param.textDocument, begin_pos, + std::make_unique(CCOpts, false), CCOpts, + callback)); } } } // namespace ccls diff --git a/src/project.cc b/src/project.cc index 911e84d2..8ea71643 100644 --- a/src/project.cc +++ b/src/project.cc @@ -141,6 +141,7 @@ struct ProjectProcessor { } args.push_back(Intern("-working-directory=" + entry.directory)); entry.args = args; +#if LLVM_VERSION_MAJOR < 8 args.push_back("-fsyntax-only"); if (!command_set.insert(hash).second) return; @@ -191,6 +192,7 @@ struct ProjectProcessor { break; } } +#endif } };