<div dir="ltr">And sorry for breaking the build, it's a non-deterministic failure that didn't show up on my test run :-(</div><div class="gmail_extra"><br><div class="gmail_quote">On Mon, May 15, 2017 at 9:02 PM, Ilya Biryukov <span dir="ltr"><<a href="mailto:ibiryukov@google.com" target="_blank">ibiryukov@google.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><div>There's a '-run-synchronously' flag forgotten in the test.</div><div>Still, it shouldn't panic.</div><div><br></div>I'll take a look at it tomorrow, thanks for reverting the commit.</div><div class="gmail_extra"><br><div class="gmail_quote">On Mon, May 15, 2017 at 8:29 PM, Adam Nemet <span dir="ltr"><<a href="mailto:anemet@apple.com" target="_blank">anemet@apple.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div style="word-wrap:break-word">Even after Simon’s fix there is still a test failure (<a href="http://lab.llvm.org:8080/green/job/clang-stage1-configure-RA_check/31377/consoleFull#18373900728254eaf0-7326-4999-85b0-388101f2d404" target="_blank">http://lab.llvm.org:8080/gree<wbr>n/job/clang-stage1-configure-<wbr>RA_check/31377/consoleFull#183<wbr>73900728254eaf0-7326-4999-85b0<wbr>-388101f2d404</a>). Reverted the original commit and the fix in r303093 and r303094.<div><br><div><blockquote type="cite"><div>On May 15, 2017, at 10:06 AM, Adam Nemet <<a href="mailto:anemet@apple.com" target="_blank">anemet@apple.com</a>> wrote:</div><br class="m_3771896676606961527m_-3724680780602895101Apple-interchange-newline"><div><div style="word-wrap:break-word">Hi Simon,<div><br></div><div>Is r303078 supposed to fix this?</div><div><br></div><div>Adam</div><div><br><div><blockquote type="cite"><div>On May 15, 2017, at 9:28 AM, Adam Nemet <<a href="mailto:anemet@apple.com" target="_blank">anemet@apple.com</a>> wrote:</div><br class="m_3771896676606961527m_-3724680780602895101Apple-interchange-newline"><div><div style="word-wrap:break-word">This broke the build <a href="http://green.lab.llvm.org/green/job/clang-stage1-configure-RA/34305/" target="_blank">http://green.lab.llvm.or<wbr>g/green/job/clang-stage1-confi<wbr>gure-RA/34305/</a>, please take a look.<div><br></div><div>Adam</div><div><br><div><blockquote type="cite"><div>On May 15, 2017, at 7:17 AM, Ilya Biryukov via cfe-commits <<a href="mailto:cfe-commits@lists.llvm.org" target="_blank">cfe-commits@lists.llvm.org</a>> wrote:</div><br class="m_3771896676606961527m_-3724680780602895101Apple-interchange-newline"><div><div>Author: ibiryukov<br>Date: Mon May 15 09:17:35 2017<br>New Revision: 303067<br><br>URL: <a href="http://llvm.org/viewvc/llvm-project?rev=303067&view=rev" target="_blank">http://llvm.org/viewvc/llvm-pr<wbr>oject?rev=303067&view=rev</a><br>Log:<br>[ClangD] Refactor clangd into separate components<br><br>Summary: Major refactoring to split LSP implementation, Clang API calls and threading(mostly synchronization)<br><br>Reviewers: bkramer, krasimir<br><br>Reviewed By: bkramer<br><br>Subscribers: cfe-commits, mgorny, klimek<br><br>Tags: #clang-tools-extra<br><br>Differential Revision: <a href="https://reviews.llvm.org/D33047" target="_blank">https://reviews.llvm.org/D3304<wbr>7</a><br><br>Added:<br> clang-tools-extra/trunk/cla<wbr>ngd/ClangdLSPServer.cpp<br> clang-tools-extra/trunk/cla<wbr>ngd/ClangdLSPServer.h<br> clang-tools-extra/trunk/cla<wbr>ngd/ClangdServer.cpp<br> - copied, changed from r303060, clang-tools-extra/trunk/clangd<wbr>/ASTManager.cpp<br> clang-tools-extra/trunk/cla<wbr>ngd/ClangdServer.h<br> - copied, changed from r303063, clang-tools-extra/trunk/clangd<wbr>/ASTManager.h<br> clang-tools-extra/trunk/cla<wbr>ngd/ClangdUnit.cpp<br> clang-tools-extra/trunk/cla<wbr>ngd/ClangdUnit.h<br> clang-tools-extra/trunk/cla<wbr>ngd/ClangdUnitStore.cpp<br> clang-tools-extra/trunk/cla<wbr>ngd/ClangdUnitStore.h<br> clang-tools-extra/trunk/cla<wbr>ngd/DraftStore.cpp<br> clang-tools-extra/trunk/cla<wbr>ngd/DraftStore.h<br> - copied, changed from r303060, clang-tools-extra/trunk/clangd<wbr>/DocumentStore.h<br> clang-tools-extra/trunk/cla<wbr>ngd/GlobalCompilationDatabase.<wbr>cpp<br> clang-tools-extra/trunk/cla<wbr>ngd/GlobalCompilationDatabase.<wbr>h<br> clang-tools-extra/trunk/cla<wbr>ngd/Path.h<br>Removed:<br> clang-tools-extra/trunk/cla<wbr>ngd/ASTManager.cpp<br> clang-tools-extra/trunk/cla<wbr>ngd/ASTManager.h<br> clang-tools-extra/trunk/cla<wbr>ngd/DocumentStore.h<br>Modified:<br> clang-tools-extra/trunk/cla<wbr>ngd/CMakeLists.txt<br> clang-tools-extra/trunk/cla<wbr>ngd/ClangdMain.cpp<br> clang-tools-extra/trunk/cla<wbr>ngd/ProtocolHandlers.cpp<br> clang-tools-extra/trunk/cla<wbr>ngd/ProtocolHandlers.h<br><br>Removed: clang-tools-extra/trunk/clangd<wbr>/ASTManager.cpp<br>URL: <a href="http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/ASTManager.cpp?rev=303066&view=auto" target="_blank">http://llvm.org/viewvc/llvm-pr<wbr>oject/clang-tools-extra/trunk/<wbr>clangd/ASTManager.cpp?rev=<wbr>303066&view=auto</a><br>==============================<wbr>==============================<wbr>==================<br>--- clang-tools-extra/trunk/clangd<wbr>/ASTManager.cpp (original)<br>+++ clang-tools-extra/trunk/clangd<wbr>/ASTManager.cpp (removed)<br>@@ -1,440 +0,0 @@<br>-//===--- ASTManager.cpp - Clang AST manager ------------------------------<wbr>-===//<br>-//<br>-// The LLVM Compiler Infrastructure<br>-//<br>-// This file is distributed under the University of Illinois Open Source<br>-// License. See LICENSE.TXT for details.<br>-//<br>-//===------------------------<wbr>------------------------------<wbr>----------------===//<br>-<br>-#include "ASTManager.h"<br>-#include "JSONRPCDispatcher.h"<br>-#include "Protocol.h"<br>-#include "clang/Frontend/ASTUnit.h"<br>-#include "clang/Frontend/CompilerInstan<wbr>ce.h"<br>-#include "clang/Tooling/CompilationData<wbr>base.h"<br>-#include "llvm/Support/Format.h"<br>-#include "llvm/Support/Path.h"<br>-#include <mutex><br>-#include <thread><br>-using namespace clang;<br>-using namespace clangd;<br>-<br>-void DocData::setAST(std::unique_pt<wbr>r<ASTUnit> AST) {<br>- this->AST = std::move(AST);<br>-}<br>-<br>-ASTUnit *DocData::getAST() const { return AST.get(); }<br>-<br>-void DocData::cacheFixIts(Diagnosti<wbr>cToReplacementMap FixIts) {<br>- this->FixIts = std::move(FixIts);<br>-}<br>-<br>-std::vector<clang::tooling::R<wbr>eplacement><br>-DocData::getFixIts(const clangd::Diagnostic &D) const {<br>- auto it = FixIts.find(D);<br>- if (it != FixIts.end())<br>- return it->second;<br>- return {};<br>-}<br>-<br>-ASTManagerRequest::ASTManager<wbr>Request(ASTManagerRequestType Type,<br>- <wbr> std::string File,<br>- <wbr> DocVersion Version)<br>- : Type(Type), File(File), Version(Version) {}<br>-<br>-/// Retrieve a copy of the contents of every file in the store, for feeding into<br>-/// ASTUnit.<br>-static std::vector<ASTUnit::RemappedF<wbr>ile><br>-getRemappedFiles(const DocumentStore &Docs) {<br>- // FIXME: Use VFS instead. This would allow us to get rid of the chdir below.<br>- std::vector<ASTUnit::Remapped<wbr>File> RemappedFiles;<br>- for (const auto &P : Docs.getAllDocuments()) {<br>- StringRef FileName = P.first;<br>- RemappedFiles.push_back(AST<wbr>Unit::RemappedFile(<br>- FileName,<br>- llvm::MemoryBuffer::get<wbr>MemBufferCopy(P.second, FileName).release()));<br>- }<br>- return RemappedFiles;<br>-}<br>-<br>-/// Convert from clang diagnostic level to LSP severity.<br>-static int getSeverity(DiagnosticsEngine:<wbr>:Level L) {<br>- switch (L) {<br>- case DiagnosticsEngine::Remark:<br>- return 4;<br>- case DiagnosticsEngine::Note:<br>- return 3;<br>- case DiagnosticsEngine::Warning:<br>- return 2;<br>- case DiagnosticsEngine::Fatal:<br>- case DiagnosticsEngine::Error:<br>- return 1;<br>- case DiagnosticsEngine::Ignored:<br>- return 0;<br>- }<br>- llvm_unreachable("Unknown diagnostic level!");<br>-}<br>-<br>-static CompletionItemKind getKind(CXCursorKind K) {<br>- switch (K) {<br>- case CXCursor_MacroInstantiation:<br>- case CXCursor_MacroDefinition:<br>- return CompletionItemKind::Text;<br>- case CXCursor_CXXMethod:<br>- return CompletionItemKind::Method;<br>- case CXCursor_FunctionDecl:<br>- case CXCursor_FunctionTemplate:<br>- return CompletionItemKind::Function;<br>- case CXCursor_Constructor:<br>- case CXCursor_Destructor:<br>- return CompletionItemKind::Constructo<wbr>r;<br>- case CXCursor_FieldDecl:<br>- return CompletionItemKind::Field;<br>- case CXCursor_VarDecl:<br>- case CXCursor_ParmDecl:<br>- return CompletionItemKind::Variable;<br>- case CXCursor_ClassDecl:<br>- case CXCursor_StructDecl:<br>- case CXCursor_UnionDecl:<br>- case CXCursor_ClassTemplate:<br>- case CXCursor_ClassTemplatePartialS<wbr>pecialization:<br>- return CompletionItemKind::Class;<br>- case CXCursor_Namespace:<br>- case CXCursor_NamespaceAlias:<br>- case CXCursor_NamespaceRef:<br>- return CompletionItemKind::Module;<br>- case CXCursor_EnumConstantDecl:<br>- return CompletionItemKind::Value;<br>- case CXCursor_EnumDecl:<br>- return CompletionItemKind::Enum;<br>- case CXCursor_TypeAliasDecl:<br>- case CXCursor_TypeAliasTemplateDecl<wbr>:<br>- case CXCursor_TypedefDecl:<br>- case CXCursor_MemberRef:<br>- case CXCursor_TypeRef:<br>- return CompletionItemKind::Reference;<br>- default:<br>- return CompletionItemKind::Missing;<br>- }<br>-}<br>-<br>-ASTManager::ASTManager(JSONOu<wbr>tput &Output, DocumentStore &Store,<br>- bool RunSynchronously)<br>- : Output(Output), Store(Store), RunSynchronously(RunSynchronou<wbr>sly),<br>- PCHs(std::make_shared<PCH<wbr>ContainerOperations>()),<br>- ClangWorker([this]() { runWorker(); }) {}<br>-<br>-void ASTManager::runWorker() {<br>- while (true) {<br>- ASTManagerRequest Request;<br>-<br>- // Pick request from the queue<br>- {<br>- std::unique_lock<std::mut<wbr>ex> Lock(RequestLock);<br>- // Wait for more requests.<br>- ClangRequestCV.wait(Lock,<br>- [this<wbr>] { return !RequestQueue.empty() || Done; });<br>- if (Done)<br>- return;<br>- assert(!RequestQueue.empt<wbr>y() && "RequestQueue was empty");<br>-<br>- Request = std::move(RequestQueue.back())<wbr>;<br>- RequestQueue.pop_back();<br>-<br>- // Skip outdated requests<br>- if (Request.Version != DocVersions.find(Request.File)<wbr>->second) {<br>- Output.log("Version for " + Twine(Request.File) +<br>- " in request is outdated, skipping request\n");<br>- continue;<br>- }<br>- } // unlock RequestLock<br>-<br>- handleRequest(Request.Type, Request.File);<br>- }<br>-}<br>-<br>-void ASTManager::queueOrRun(ASTMana<wbr>gerRequestType RequestType, StringRef File) {<br>- if (RunSynchronously) {<br>- handleRequest(RequestType, File);<br>- return;<br>- }<br>-<br>- std::lock_guard<std::mutex> Guard(RequestLock);<br>- // We increment the version of the added document immediately and schedule<br>- // the requested operation to be run on a worker thread<br>- DocVersion version = ++DocVersions[File];<br>- RequestQueue.push_back(ASTMan<wbr>agerRequest(RequestType, File, version));<br>- ClangRequestCV.notify_one();<br>-}<br>-<br>-void ASTManager::handleRequest(ASTM<wbr>anagerRequestType RequestType,<br>- <wbr>StringRef File) {<br>- switch (RequestType) {<br>- case ASTManagerRequestType::ParseAn<wbr>dPublishDiagnostics:<br>- parseFileAndPublishDiagnost<wbr>ics(File);<br>- break;<br>- case ASTManagerRequestType::RemoveD<wbr>ocData: {<br>- std::lock_guard<std::mutex> Lock(ClangObjectLock);<br>- auto DocDataIt = DocDatas.find(File);<br>- // We could get the remove request before parsing for the document is<br>- // started, just do nothing in that case, parsing request will be discarded<br>- // because it has a lower version value<br>- if (DocDataIt == DocDatas.end())<br>- return;<br>- DocDatas.erase(DocDataIt);<br>- break;<br>- } // unlock ClangObjectLock<br>- }<br>-}<br>-<br>-void ASTManager::parseFileAndPublis<wbr>hDiagnostics(StringRef File) {<br>- std::unique_lock<std::mutex> ClangObjectLockGuard(ClangObje<wbr>ctLock);<br>-<br>- auto &DocData = DocDatas[File];<br>- ASTUnit *Unit = DocData.getAST();<br>- if (!Unit) {<br>- auto newAST = createASTUnitForFile(File, this->Store);<br>- Unit = newAST.get();<br>-<br>- DocData.setAST(std::move(ne<wbr>wAST));<br>- } else {<br>- // Do a reparse if this wasn't the first parse.<br>- // FIXME: This might have the wrong working directory if it changed in the<br>- // meantime.<br>- Unit->Reparse(PCHs, getRemappedFiles(this->Store))<wbr>;<br>- }<br>-<br>- if (!Unit)<br>- return;<br>-<br>- // Send the diagnotics to the editor.<br>- // FIXME: If the diagnostic comes from a different file, do we want to<br>- // show them all? Right now we drop everything not coming from the<br>- // main file.<br>- std::string Diagnostics;<br>- DocData::DiagnosticToReplacem<wbr>entMap LocalFixIts; // Temporary storage<br>- for (ASTUnit::stored_diag_iterator D = Unit->stored_diag_begin(),<br>- <wbr> DEnd = Unit->stored_diag_end();<br>- D != DEnd; ++D) {<br>- if (!D->getLocation().isValid() ||<br>- !D->getLocation().getMa<wbr>nager().isInMainFile(D->getLoc<wbr>ation()))<br>- continue;<br>- Position P;<br>- P.line = D->getLocation().getSpellingLi<wbr>neNumber() - 1;<br>- P.character = D->getLocation().getSpellingCo<wbr>lumnNumber();<br>- Range R = {P, P};<br>- Diagnostics +=<br>- R"({"range":)" + Range::unparse(R) +<br>- R"(,"severity":)" + std::to_string(getSeverity(D-><wbr>getLevel())) +<br>- R"(,"message":")" + llvm::yaml::escape(D->getMessa<wbr>ge()) +<br>- R"("},)";<br>-<br>- // We convert to Replacements to become independent of the SourceManager.<br>- clangd::Diagnostic Diag = {R, getSeverity(D->getLevel()), D->getMessage()};<br>- auto &FixItsForDiagnostic = LocalFixIts[Diag];<br>- for (const FixItHint &Fix : D->getFixIts()) {<br>- FixItsForDiagnostic.push_<wbr>back(clang::tooling::Replaceme<wbr>nt(<br>- Unit->getSourceManage<wbr>r(), Fix.RemoveRange, Fix.CodeToInsert));<br>- }<br>- }<br>-<br>- // Put FixIts into place.<br>- DocData.cacheFixIts(std::move<wbr>(LocalFixIts));<br>-<br>- ClangObjectLockGuard.unlock()<wbr>;<br>- // No accesses to clang objects are allowed after this point.<br>-<br>- // Publish diagnostics.<br>- if (!Diagnostics.empty())<br>- Diagnostics.pop_back(); // Drop trailing comma.<br>- Output.writeMessage(<br>- R"({"jsonrpc":"2.0","meth<wbr>od":"textDocument/publishDiagn<wbr>ostics","params":{"uri":")" +<br>- URI::fromFile(File).uri + R"(","diagnostics":[)" + Diagnostics + R"(]}})");<br>-}<br>-<br>-ASTManager::~ASTManager() {<br>- {<br>- std::lock_guard<std::mutex> Guard(RequestLock);<br>- // Wake up the clang worker thread, then exit.<br>- Done = true;<br>- ClangRequestCV.notify_one()<wbr>;<br>- } // unlock DocDataLock<br>- ClangWorker.join();<br>-}<br>-<br>-void ASTManager::onDocumentAdd(Stri<wbr>ngRef File) {<br>- queueOrRun(ASTManagerRequestT<wbr>ype::ParseAndPublishDiagnostic<wbr>s, File);<br>-}<br>-<br>-void ASTManager::onDocumentRemove(S<wbr>tringRef File) {<br>- queueOrRun(ASTManagerRequestT<wbr>ype::RemoveDocData, File);<br>-}<br>-<br>-tooling::CompilationDatabase *<br>-ASTManager::getOrCreateCompil<wbr>ationDatabaseForFile(StringRef File) {<br>- namespace path = llvm::sys::path;<br>-<br>- assert((path::is_absolute(Fil<wbr>e, path::Style::posix) ||<br>- path::is_absolute(Fil<wbr>e, path::Style::windows)) &&<br>- "path must be absolute");<br>-<br>- for (auto Path = path::parent_path(File); !Path.empty();<br>- Path = path::parent_path(Path)) {<br>-<br>- auto CachedIt = CompilationDatabases.find(Path<wbr>);<br>- if (CachedIt != CompilationDatabases.end())<br>- return CachedIt->second.get();<br>- std::string Error;<br>- auto CDB = tooling::CompilationDatabase::<wbr>loadFromDirectory(Path, Error);<br>- if (!CDB) {<br>- if (!Error.empty()) {<br>- Output.log("Error when trying to load compilation database from " +<br>- Twine(Path) + ": " + Twine(Error) + "\n");<br>- }<br>- continue;<br>- }<br>-<br>- // TODO(ibiryukov): Invalidate cached compilation databases on changes<br>- auto result = CDB.get();<br>- CompilationDatabases.insert<wbr>(std::make_pair(Path, std::move(CDB)));<br>- return result;<br>- }<br>-<br>- Output.log("Failed to find compilation database for " + Twine(File) + "\n");<br>- return nullptr;<br>-}<br>-<br>-std::unique_ptr<clang::ASTUni<wbr>t><br>-ASTManager::createASTUnitForF<wbr>ile(StringRef File, const DocumentStore &Docs) {<br>- tooling::CompilationDatabase *CDB =<br>- getOrCreateCompilationDat<wbr>abaseForFile(File);<br>-<br>- std::vector<tooling::CompileC<wbr>ommand> Commands;<br>-<br>- if (CDB) {<br>- Commands = CDB->getCompileCommands(File);<br>- // chdir. This is thread hostile.<br>- if (!Commands.empty())<br>- llvm::sys::fs::set_curren<wbr>t_path(Commands.front().Direct<wbr>ory);<br>- }<br>- if (Commands.empty()) {<br>- // Add a fake command line if we know nothing.<br>- Commands.push_back(tooling:<wbr>:CompileCommand(<br>- llvm::sys::path::parent<wbr>_path(File), llvm::sys::path::filename(File<wbr>),<br>- {"clang", "-fsyntax-only", File.str()}, ""));<br>- }<br>-<br>- // Inject the resource dir.<br>- // FIXME: Don't overwrite it if it's already there.<br>- static int Dummy; // Just an address in this process.<br>- std::string ResourceDir =<br>- CompilerInvocation::GetRe<wbr>sourcesPath("clangd", (void *)&Dummy);<br>- Commands.front().CommandLine.<wbr>push_back("-resource-dir=" + ResourceDir);<br>-<br>- IntrusiveRefCntPtr<Diagnostic<wbr>sEngine> Diags =<br>- CompilerInstance::createD<wbr>iagnostics(new DiagnosticOptions);<br>-<br>- std::vector<const char *> ArgStrs;<br>- for (const auto &S : Commands.front().CommandLine)<br>- ArgStrs.push_back(S.c_str()<wbr>);<br>-<br>- auto ArgP = &*ArgStrs.begin();<br>- return std::unique_ptr<clang::ASTUnit<wbr>>(ASTUnit::LoadFromCommandLine<wbr>(<br>- ArgP, ArgP + ArgStrs.size(), PCHs, Diags, ResourceDir,<br>- /*OnlyLocalDecls=*/false, /*CaptureDiagnostics=*/true,<br>- getRemappedFiles(Docs),<br>- /*RemappedFilesKeepOrigin<wbr>alName=*/true,<br>- /*PrecompilePreambleAfter<wbr>NParses=*/1, /*TUKind=*/TU_Complete,<br>- /*CacheCodeCompletionResu<wbr>lts=*/true,<br>- /*IncludeBriefCommentsInC<wbr>odeCompletion=*/true,<br>- /*AllowPCHWithCompilerErr<wbr>ors=*/true));<br>-}<br>-<br>-std::vector<clang::tooling::R<wbr>eplacement><br>-ASTManager::getFixIts(StringR<wbr>ef File, const clangd::Diagnostic &D) {<br>- // TODO(ibiryukov): the FixIts should be available immediately<br>- // even when parsing is being run on a worker thread<br>- std::lock_guard<std::mutex> Guard(ClangObjectLock);<br>- return DocDatas[File].getFixIts(D);<br>-}<br>-<br>-namespace {<br>-class CompletionItemsCollector : public CodeCompleteConsumer {<br>- std::vector<CompletionItem> *Items;<br>- std::shared_ptr<clang::Global<wbr>CodeCompletionAllocator> Allocator;<br>- CodeCompletionTUInfo CCTUInfo;<br>-<br>-public:<br>- CompletionItemsCollector(std:<wbr>:vector<CompletionItem> *Items,<br>- cons<wbr>t CodeCompleteOptions &CodeCompleteOpts)<br>- : CodeCompleteConsumer(CodeCompl<wbr>eteOpts, /*OutputIsBinary=*/false),<br>- Items(Items),<br>- Allocator(std::make_sha<wbr>red<clang::GlobalCodeCompletio<wbr>nAllocator>()),<br>- CCTUInfo(Allocator) {}<br>-<br>- void ProcessCodeCompleteResults(Sem<wbr>a &S, CodeCompletionContext Context,<br>- <wbr> CodeCompletionResult *Results,<br>- <wbr> unsigned NumResults) override {<br>- for (unsigned I = 0; I != NumResults; ++I) {<br>- CodeCompletionResult &Result = Results[I];<br>- CodeCompletionString *CCS = Result.CreateCodeCompletionStr<wbr>ing(<br>- S, Context, *Allocator, CCTUInfo,<br>- CodeCompleteOpts.Incl<wbr>udeBriefComments);<br>- if (CCS) {<br>- CompletionItem Item;<br>- assert(CCS->getTypedTex<wbr>t());<br>- Item.label = CCS->getTypedText();<br>- Item.kind = getKind(Result.CursorKind);<br>- if (CCS->getBriefComment())<br>- Item.documentation = CCS->getBriefComment();<br>- Items->push_back(std::m<wbr>ove(Item));<br>- }<br>- }<br>- }<br>-<br>- GlobalCodeCompletionAllocator &getAllocator() override { return *Allocator; }<br>-<br>- CodeCompletionTUInfo &getCodeCompletionTUInfo() override { return CCTUInfo; }<br>-};<br>-<br>-} // namespace<br>-<br>-std::vector<CompletionItem><br>-ASTManager::codeComplete(Stri<wbr>ngRef File, unsigned Line, unsigned Column) {<br>- CodeCompleteOptions CCO;<br>- CCO.IncludeBriefComments = 1;<br>- // This is where code completion stores dirty buffers. Need to free after<br>- // completion.<br>- SmallVector<const llvm::MemoryBuffer *, 4> OwnedBuffers;<br>- SmallVector<StoredDiagnostic, 4> StoredDiagnostics;<br>- IntrusiveRefCntPtr<Diagnostic<wbr>sEngine> DiagEngine(<br>- new DiagnosticsEngine(new DiagnosticIDs, new DiagnosticOptions));<br>- std::vector<CompletionItem> Items;<br>- CompletionItemsCollector Collector(&Items, CCO);<br>-<br>- std::lock_guard<std::mutex> Guard(ClangObjectLock);<br>- auto &DocData = DocDatas[File];<br>- auto Unit = DocData.getAST();<br>- if (!Unit) {<br>- auto newAST = createASTUnitForFile(File, this->Store);<br>- Unit = newAST.get();<br>- DocData.setAST(std::move(ne<wbr>wAST));<br>- }<br>- if (!Unit)<br>- return {};<br>- IntrusiveRefCntPtr<SourceMana<wbr>ger> SourceMgr(<br>- new SourceManager(*DiagEngine, Unit->getFileManager()));<br>- // CodeComplete seems to require fresh LangOptions.<br>- LangOptions LangOpts = Unit->getLangOpts();<br>- // The language server protocol uses zero-based line and column numbers.<br>- // The clang code completion uses one-based numbers.<br>- Unit->CodeComplete(File, Line + 1, Column + 1, getRemappedFiles(this->Store),<br>- CCO.Includ<wbr>eMacros, CCO.IncludeCodePatterns,<br>- CCO.Includ<wbr>eBriefComments, Collector, PCHs, *DiagEngine,<br>- LangOpts, *SourceMgr, Unit->getFileManager(),<br>- StoredDiag<wbr>nostics, OwnedBuffers);<br>- for (const llvm::MemoryBuffer *Buffer : OwnedBuffers)<br>- delete Buffer;<br>- return Items;<br>-}<br><br>Removed: clang-tools-extra/trunk/clangd<wbr>/ASTManager.h<br>URL: <a href="http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/ASTManager.h?rev=303066&view=auto" target="_blank">http://llvm.org/viewvc/llvm-pr<wbr>oject/clang-tools-extra/trunk/<wbr>clangd/ASTManager.h?rev=303066<wbr>&view=auto</a><br>==============================<wbr>==============================<wbr>==================<br>--- clang-tools-extra/trunk/clangd<wbr>/ASTManager.h (original)<br>+++ clang-tools-extra/trunk/clangd<wbr>/ASTManager.h (removed)<br>@@ -1,162 +0,0 @@<br>-//===--- ASTManager.h - Clang AST manager -----------------------*- C++ -*-===//<br>-//<br>-// The LLVM Compiler Infrastructure<br>-//<br>-// This file is distributed under the University of Illinois Open Source<br>-// License. See LICENSE.TXT for details.<br>-//<br>-//===------------------------<wbr>------------------------------<wbr>----------------===//<br>-<br>-#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_<wbr>ASTMANAGER_H<br>-#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_<wbr>ASTMANAGER_H<br>-<br>-#include "DocumentStore.h"<br>-#include "JSONRPCDispatcher.h"<br>-#include "Protocol.h"<br>-#include "clang/Tooling/Core/Replacemen<wbr>t.h"<br>-#include "llvm/ADT/IntrusiveRefCntPtr.h<wbr>"<br>-#include <condition_variable><br>-#include <deque><br>-#include <thread><br>-<br>-namespace clang {<br>-class ASTUnit;<br>-class DiagnosticsEngine;<br>-class PCHContainerOperations;<br>-namespace tooling {<br>-class CompilationDatabase;<br>-} // namespace tooling<br>-<br>-namespace clangd {<br>-<br>-/// Using 'unsigned' here to avoid undefined behaviour on overflow.<br>-typedef unsigned DocVersion;<br>-<br>-/// Stores ASTUnit and FixIts map for an opened document.<br>-class DocData {<br>-public:<br>- typedef std::map<clangd::Diagnostic, std::vector<clang::tooling::Re<wbr>placement>><br>- DiagnosticToReplacementMa<wbr>p;<br>-<br>-public:<br>- void setAST(std::unique_ptr<ASTUnit<wbr>> AST);<br>- ASTUnit *getAST() const;<br>-<br>- void cacheFixIts(DiagnosticToReplac<wbr>ementMap FixIts);<br>- std::vector<clang::tooling::R<wbr>eplacement><br>- getFixIts(const clangd::Diagnostic &D) const;<br>-<br>-private:<br>- std::unique_ptr<ASTUnit> AST;<br>- DiagnosticToReplacementMap FixIts;<br>-};<br>-<br>-enum class ASTManagerRequestType { ParseAndPublishDiagnostics, RemoveDocData };<br>-<br>-/// A request to the worker thread<br>-class ASTManagerRequest {<br>-public:<br>- ASTManagerRequest() = default;<br>- ASTManagerRequest(ASTManagerR<wbr>equestType Type, std::string File,<br>- DocVersion Version);<br>-<br>- ASTManagerRequestType Type;<br>- std::string File;<br>- DocVersion Version;<br>-};<br>-<br>-class ASTManager : public DocumentStoreListener {<br>-public:<br>- ASTManager(JSONOutput &Output, DocumentStore &Store, bool RunSynchronously);<br>- ~ASTManager() override;<br>-<br>- void onDocumentAdd(StringRef File) override;<br>- void onDocumentRemove(StringRef File) override;<br>-<br>- /// Get code completions at a specified \p Line and \p Column in \p File.<br>- ///<br>- /// This function is thread-safe and returns completion items that own the<br>- /// data they contain.<br>- std::vector<CompletionItem> codeComplete(StringRef File, unsigned Line,<br>- <wbr> unsigned Column);<br>-<br>- /// Get the fixes associated with a certain diagnostic in a specified file as<br>- /// replacements.<br>- ///<br>- /// This function is thread-safe. It returns a copy to avoid handing out<br>- /// references to unguarded data.<br>- std::vector<clang::tooling::R<wbr>eplacement><br>- getFixIts(StringRef File, const clangd::Diagnostic &D);<br>-<br>- DocumentStore &getStore() const { return Store; }<br>-<br>-private:<br>- JSONOutput &Output;<br>- DocumentStore &Store;<br>-<br>- // Set to true if requests should block instead of being processed<br>- // asynchronously.<br>- bool RunSynchronously;<br>-<br>- /// Loads a compilation database for File. May return nullptr if it fails. The<br>- /// database is cached for subsequent accesses.<br>- clang::tooling::CompilationDa<wbr>tabase *<br>- getOrCreateCompilationDatabas<wbr>eForFile(StringRef File);<br>- // Creates a new ASTUnit for the document at File.<br>- // FIXME: This calls chdir internally, which is thread unsafe.<br>- std::unique_ptr<clang::ASTUni<wbr>t><br>- createASTUnitForFile(StringRe<wbr>f File, const DocumentStore &Docs);<br>-<br>- /// If RunSynchronously is false, queues the request to be run on the worker<br>- /// thread.<br>- /// If RunSynchronously is true, runs the request handler immediately on the<br>- /// main thread.<br>- void queueOrRun(ASTManagerRequestTy<wbr>pe RequestType, StringRef File);<br>-<br>- void runWorker();<br>- void handleRequest(ASTManagerReques<wbr>tType RequestType, StringRef File);<br>-<br>- /// Parses files and publishes diagnostics.<br>- /// This function is called on the worker thread in asynchronous mode and<br>- /// on the main thread in synchronous mode.<br>- void parseFileAndPublishDiagnostics<wbr>(StringRef File);<br>-<br>- /// Caches compilation databases loaded from directories(keys are directories).<br>- llvm::StringMap<std::unique_p<wbr>tr<clang::tooling::Compilation<wbr>Database>><br>- CompilationDatabases;<br>-<br>- /// Clang objects.<br>- /// A map from filenames to DocData structures that store ASTUnit and Fixits for<br>- /// the files. The ASTUnits are used for generating diagnostics and fix-it-s<br>- /// asynchronously by the worker thread and synchronously for code completion.<br>- llvm::StringMap<DocData> DocDatas;<br>- std::shared_ptr<clang::PCHCon<wbr>tainerOperations> PCHs;<br>- /// A lock for access to the DocDatas, CompilationDatabases and PCHs.<br>- std::mutex ClangObjectLock;<br>-<br>- /// Stores latest versions of the tracked documents to discard outdated requests.<br>- /// Guarded by RequestLock.<br>- /// TODO(ibiryukov): the entries are neved deleted from this map.<br>- llvm::StringMap<DocVersion> DocVersions;<br>-<br>- /// A LIFO queue of requests. Note that requests are discarded if the `version`<br>- /// field is not equal to the one stored inside DocVersions.<br>- /// TODO(krasimir): code completion should always have priority over parsing<br>- /// for diagnostics.<br>- std::deque<ASTManagerRequest> RequestQueue;<br>- /// Setting Done to true will make the worker thread terminate.<br>- bool Done = false;<br>- /// Condition variable to wake up the worker thread.<br>- std::condition_variable ClangRequestCV;<br>- /// Lock for accesses to RequestQueue, DocVersions and Done.<br>- std::mutex RequestLock;<br>-<br>- /// We run parsing on a separate thread. This thread looks into RequestQueue to<br>- /// find requests to handle and terminates when Done is set to true.<br>- std::thread ClangWorker;<br>-};<br>-<br>-} // namespace clangd<br>-} // namespace clang<br>-<br>-#endif<br><br>Modified: clang-tools-extra/trunk/clangd<wbr>/CMakeLists.txt<br>URL: <a href="http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/CMakeLists.txt?rev=303067&r1=303066&r2=303067&view=diff" target="_blank">http://llvm.org/viewvc/llvm-pr<wbr>oject/clang-tools-extra/trunk/<wbr>clangd/CMakeLists.txt?rev=<wbr>303067&r1=303066&r2=303067&<wbr>view=diff</a><br>==============================<wbr>==============================<wbr>==================<br>--- clang-tools-extra/trunk/clangd<wbr>/CMakeLists.txt (original)<br>+++ clang-tools-extra/trunk/clangd<wbr>/CMakeLists.txt Mon May 15 09:17:35 2017<br>@@ -1,6 +1,11 @@<br> add_clang_executable(clangd<br>- ASTManager.cpp<br>+ ClangdLSPServer.cpp<br> ClangdMain.cpp<br>+ ClangdServer.cpp<br>+ ClangdUnit.cpp<br>+ ClangdUnitStore.cpp<br>+ DraftStore.cpp<br>+ GlobalCompilationDatabase.cpp<br> JSONRPCDispatcher.cpp<br> Protocol.cpp<br> ProtocolHandlers.cpp<br><br>Added: clang-tools-extra/trunk/clangd<wbr>/ClangdLSPServer.cpp<br>URL: <a href="http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/ClangdLSPServer.cpp?rev=303067&view=auto" target="_blank">http://llvm.org/viewvc/llvm-pr<wbr>oject/clang-tools-extra/trunk/<wbr>clangd/ClangdLSPServer.cpp?<wbr>rev=303067&view=auto</a><br>==============================<wbr>==============================<wbr>==================<br>--- clang-tools-extra/trunk/clangd<wbr>/ClangdLSPServer.cpp (added)<br>+++ clang-tools-extra/trunk/clangd<wbr>/ClangdLSPServer.cpp Mon May 15 09:17:35 2017<br>@@ -0,0 +1,100 @@<br>+//===--- ClangdLSPServer.cpp - LSP server ------------------------*- C++-*-===//<br>+//<br>+// The LLVM Compiler Infrastructure<br>+//<br>+// This file is distributed under the University of Illinois Open Source<br>+// License. See LICENSE.TXT for details.<br>+//<br>+//===------------------------<wbr>------------------------------<wbr>---------------===//<br>+<br>+#include "ClangdLSPServer.h"<br>+#include "JSONRPCDispatcher.h"<br>+<br>+using namespace clang::clangd;<br>+using namespace clang;<br>+<br>+class ClangdLSPServer::LSPDiagnostic<wbr>sConsumer : public DiagnosticsConsumer {<br>+public:<br>+ LSPDiagnosticsConsumer(Clangd<wbr>LSPServer &Server) : Server(Server) {}<br>+<br>+ virtual void onDiagnosticsReady(PathRef File,<br>+ <wbr> std::vector<DiagWithFixIts> Diagnostics) {<br>+ Server.consumeDiagnostics(F<wbr>ile, Diagnostics);<br>+ }<br>+<br>+private:<br>+ ClangdLSPServer &Server;<br>+};<br>+<br>+ClangdLSPServer::ClangdLSPSer<wbr>ver(JSONOutput &Out, bool RunSynchronously)<br>+ : Out(Out),<br>+ Server(llvm::make_unique<<wbr>DirectoryBasedGlobalCompilatio<wbr>nDatabase>(),<br>+ llvm::make_unique<<wbr>LSPDiagnosticsConsumer>(*this)<wbr>,<br>+ RunSynchronously) {}<br>+<br>+void ClangdLSPServer::openDocument(<wbr>StringRef File, StringRef Contents) {<br>+ Server.addDocument(File, Contents);<br>+}<br>+<br>+void ClangdLSPServer::closeDocument<wbr>(StringRef File) {<br>+ Server.removeDocument(File);<br>+}<br>+<br>+std::vector<CompletionItem> ClangdLSPServer::codeComplete(<wbr>PathRef File,<br>+ <wbr> Pos<wbr>ition Pos) {<br>+ return Server.codeComplete(File, Pos);<br>+}<br>+<br>+std::vector<clang::tooling::R<wbr>eplacement><br>+ClangdLSPServer::getFixIts(St<wbr>ringRef File, const clangd::Diagnostic &D) {<br>+ std::lock_guard<std::mutex> Lock(FixItsMutex);<br>+ auto DiagToFixItsIter = FixItsMap.find(File);<br>+ if (DiagToFixItsIter == FixItsMap.end())<br>+ return {};<br>+<br>+ const auto &DiagToFixItsMap = DiagToFixItsIter->second;<br>+ auto FixItsIter = DiagToFixItsMap.find(D);<br>+ if (FixItsIter == DiagToFixItsMap.end())<br>+ return {};<br>+<br>+ return FixItsIter->second;<br>+}<br>+<br>+std::string ClangdLSPServer::getDocument(P<wbr>athRef File) {<br>+ return Server.getDocument(File);<br>+}<br>+<br>+void ClangdLSPServer::consumeDiagno<wbr>stics(<br>+ PathRef File, std::vector<DiagWithFixIts> Diagnostics) {<br>+ std::string DiagnosticsJSON;<br>+<br>+ DiagnosticToReplacementMap LocalFixIts; // Temporary storage<br>+ for (auto &DiagWithFixes : Diagnostics) {<br>+ auto Diag = DiagWithFixes.Diag;<br>+ DiagnosticsJSON +=<br>+ R"({"range":)" + Range::unparse(Diag.range) +<br>+ R"(,"severity":)" + std::to_string(Diag.severity) +<br>+ R"(,"message":")" + llvm::yaml::escape(Diag.messag<wbr>e) +<br>+ R"("},)";<br>+<br>+ // We convert to Replacements to become independent of the SourceManager.<br>+ auto &FixItsForDiagnostic = LocalFixIts[Diag];<br>+ std::copy(DiagWithFixes.Fix<wbr>Its.begin(), DiagWithFixes.FixIts.end(),<br>+ std::back_inserte<wbr>r(FixItsForDiagnostic));<br>+ }<br>+<br>+ // Cache FixIts<br>+ {<br>+ // FIXME(ibiryukov): should be deleted when documents are removed<br>+ std::lock_guard<std::mutex> Lock(FixItsMutex);<br>+ FixItsMap[File] = LocalFixIts;<br>+ }<br>+<br>+ // Publish diagnostics.<br>+ if (!DiagnosticsJSON.empty())<br>+ DiagnosticsJSON.pop_back(); // Drop trailing comma.<br>+ Out.writeMessage(<br>+ R"({"jsonrpc":"2.0","meth<wbr>od":"textDocument/publishDiagn<wbr>ostics","params":{"uri":")" +<br>+ URI::fromFile(File).uri + R"(","diagnostics":[)" + DiagnosticsJSON +<br>+ R"(]}})");<br>+}<br><br>Added: clang-tools-extra/trunk/clangd<wbr>/ClangdLSPServer.h<br>URL: <a href="http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/ClangdLSPServer.h?rev=303067&view=auto" target="_blank">http://llvm.org/viewvc/llvm-pr<wbr>oject/clang-tools-extra/trunk/<wbr>clangd/ClangdLSPServer.h?rev=<wbr>303067&view=auto</a><br>==============================<wbr>==============================<wbr>==================<br>--- clang-tools-extra/trunk/clangd<wbr>/ClangdLSPServer.h (added)<br>+++ clang-tools-extra/trunk/clangd<wbr>/ClangdLSPServer.h Mon May 15 09:17:35 2017<br>@@ -0,0 +1,79 @@<br>+//===--- ClangdLSPServer.h - LSP server --------------------------*- C++-*-===//<br>+//<br>+// The LLVM Compiler Infrastructure<br>+//<br>+// This file is distributed under the University of Illinois Open Source<br>+// License. See LICENSE.TXT for details.<br>+//<br>+//===------------------------<wbr>------------------------------<wbr>---------------===//<br>+<br>+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_<wbr>CLANGDLSPSERVER_H<br>+#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_<wbr>CLANGDLSPSERVER_H<br>+<br>+#include "ClangdServer.h"<br>+#include "Path.h"<br>+#include "Protocol.h"<br>+#include "clang/Tooling/Core/Replacemen<wbr>t.h"<br>+<br>+namespace clang {<br>+namespace clangd {<br>+<br>+class JSONOutput;<br>+<br>+/// This class serves as an intermediate layer of LSP server implementation,<br>+/// glueing the JSON LSP protocol layer and ClangdServer together. It doesn't<br>+/// directly handle input from LSP client.<br>+/// Most methods are synchronous and return their result directly, but<br>+/// diagnostics are provided asynchronously when ready via<br>+/// JSONOutput::writeMessage.<br>+class ClangdLSPServer {<br>+public:<br>+ ClangdLSPServer(JSONOutput &Out, bool RunSynchronously);<br>+<br>+ /// Update the document text for \p File with \p Contents, schedule update of<br>+ /// diagnostics. Out.writeMessage will called to push diagnostics to LSP<br>+ /// client asynchronously when they are ready.<br>+ void openDocument(PathRef File, StringRef Contents);<br>+ /// Stop tracking the document for \p File.<br>+ void closeDocument(PathRef File);<br>+<br>+ /// Run code completion synchronously.<br>+ std::vector<CompletionItem> codeComplete(PathRef File, Position Pos);<br>+<br>+ /// Get the fixes associated with a certain diagnostic in a specified file as<br>+ /// replacements.<br>+ ///<br>+ /// This function is thread-safe. It returns a copy to avoid handing out<br>+ /// references to unguarded data.<br>+ std::vector<clang::tooling::R<wbr>eplacement><br>+ getFixIts(StringRef File, const clangd::Diagnostic &D);<br>+<br>+ /// Get the current document contents stored for \p File.<br>+ /// FIXME(ibiryukov): This function is here to allow implementation of<br>+ /// formatCode from ProtocolHandlers.cpp. We should move formatCode to<br>+ /// ClangdServer class and remove this function from public interface.<br>+ std::string getDocument(PathRef File);<br>+<br>+private:<br>+ class LSPDiagnosticsConsumer;<br>+<br>+ /// Function that will be called on a separate thread when diagnostics are<br>+ /// ready. Sends the Dianostics to LSP client via Out.writeMessage and caches<br>+ /// corresponding fixits in the FixItsMap.<br>+ void consumeDiagnostics(PathRef File,<br>+ std::<wbr>vector<DiagWithFixIts> Diagnostics);<br>+<br>+ JSONOutput &Out;<br>+ ClangdServer Server;<br>+<br>+ std::mutex FixItsMutex;<br>+ typedef std::map<clangd::Diagnostic, std::vector<clang::tooling::Re<wbr>placement>><br>+ DiagnosticToReplacementMa<wbr>p;<br>+ /// Caches FixIts per file and diagnostics<br>+ llvm::StringMap<DiagnosticToR<wbr>eplacementMap> FixItsMap;<br>+};<br>+<br>+} // namespace clangd<br>+} // namespace clang<br>+<br>+#endif<br><br>Modified: clang-tools-extra/trunk/clangd<wbr>/ClangdMain.cpp<br>URL: <a href="http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/ClangdMain.cpp?rev=303067&r1=303066&r2=303067&view=diff" target="_blank">http://llvm.org/viewvc/llvm-pr<wbr>oject/clang-tools-extra/trunk/<wbr>clangd/ClangdMain.cpp?rev=<wbr>303067&r1=303066&r2=303067&<wbr>view=diff</a><br>==============================<wbr>==============================<wbr>==================<br>--- clang-tools-extra/trunk/clangd<wbr>/ClangdMain.cpp (original)<br>+++ clang-tools-extra/trunk/clangd<wbr>/ClangdMain.cpp Mon May 15 09:17:35 2017<br>@@ -7,15 +7,19 @@<br> //<br> //===-------------------------<wbr>------------------------------<wbr>---------------===//<br><br>-#include "ASTManager.h"<br>-#include "DocumentStore.h"<br> #include "JSONRPCDispatcher.h"<br>+#include "ClangdLSPServer.h"<br>+#include "Protocol.h"<br> #include "ProtocolHandlers.h"<br> #include "llvm/Support/CommandLine.h"<br> #include "llvm/Support/FileSystem.h"<br> #include "llvm/Support/Program.h"<br>+<br> #include <iostream><br>+#include <memory><br> #include <string><br>+<br>+using namespace clang;<br> using namespace clang::clangd;<br><br> static llvm::cl::opt<bool><br>@@ -34,9 +38,7 @@ int main(int argc, char *argv[]) {<br><br> // Set up a document store and intialize all the method handlers for JSONRPC<br> // dispatching.<br>- DocumentStore Store;<br>- ASTManager AST(Out, Store, RunSynchronously);<br>- Store.addListener(&AST);<br>+ ClangdLSPServer LSPServer(Out, RunSynchronously);<br> JSONRPCDispatcher Dispatcher(llvm::make_unique<H<wbr>andler>(Out));<br> Dispatcher.registerHandler("<wbr>initialize",<br> l<wbr>lvm::make_unique<InitializeHan<wbr>dler>(Out));<br>@@ -45,26 +47,26 @@ int main(int argc, char *argv[]) {<br> Dispatcher.registerHandler("<wbr>shutdown", std::move(ShutdownPtr));<br> Dispatcher.registerHandler(<br> "textDocument/didOpen",<br>- llvm::make_unique<TextDoc<wbr>umentDidOpenHandler>(Out, Store));<br>+ llvm::make_unique<TextDoc<wbr>umentDidOpenHandler>(Out, LSPServer));<br> Dispatcher.registerHandler(<br> "textDocument/didClose",<br>- llvm::make_unique<TextDoc<wbr>umentDidCloseHandler>(Out, Store));<br>+ llvm::make_unique<TextDoc<wbr>umentDidCloseHandler>(Out, LSPServer));<br> Dispatcher.registerHandler(<br> "textDocument/didChange"<wbr>,<br>- llvm::make_unique<TextDoc<wbr>umentDidChangeHandler>(Out, Store));<br>+ llvm::make_unique<TextDoc<wbr>umentDidChangeHandler>(Out, LSPServer));<br> Dispatcher.registerHandler(<br> "textDocument/rangeForma<wbr>tting",<br>- llvm::make_unique<TextDoc<wbr>umentRangeFormattingHandler>(<wbr>Out, Store));<br>+ llvm::make_unique<TextDoc<wbr>umentRangeFormattingHandler>(<wbr>Out, LSPServer));<br> Dispatcher.registerHandler(<br> "textDocument/onTypeForm<wbr>atting",<br>- llvm::make_unique<TextDoc<wbr>umentOnTypeFormattingHandler>(<wbr>Out, Store));<br>+ llvm::make_unique<TextDoc<wbr>umentOnTypeFormattingHandler>(<wbr>Out, LSPServer));<br> Dispatcher.registerHandler(<br> "textDocument/formatting<wbr>",<br>- llvm::make_unique<TextDoc<wbr>umentFormattingHandler>(Out, Store));<br>+ llvm::make_unique<TextDoc<wbr>umentFormattingHandler>(Out, LSPServer));<br> Dispatcher.registerHandler("<wbr>textDocument/codeAction",<br>- ll<wbr>vm::make_unique<CodeActionHand<wbr>ler>(Out, AST));<br>+ ll<wbr>vm::make_unique<CodeActionHand<wbr>ler>(Out, LSPServer));<br> Dispatcher.registerHandler("<wbr>textDocument/completion",<br>- ll<wbr>vm::make_unique<CompletionHand<wbr>ler>(Out, AST));<br>+ ll<wbr>vm::make_unique<CompletionHand<wbr>ler>(Out, LSPServer));<br><br> while (std::cin.good()) {<br> // A Language Server Protocol message starts with a HTTP header, delimited<br><br>Copied: clang-tools-extra/trunk/clangd<wbr>/ClangdServer.cpp (from r303060, clang-tools-extra/trunk/clangd<wbr>/ASTManager.cpp)<br>URL: <a href="http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/ClangdServer.cpp?p2=clang-tools-extra/trunk/clangd/ClangdServer.cpp&p1=clang-tools-extra/trunk/clangd/ASTManager.cpp&r1=303060&r2=303067&rev=303067&view=diff" target="_blank">http://llvm.org/viewvc/llvm-pr<wbr>oject/clang-tools-extra/trunk/<wbr>clangd/ClangdServer.cpp?p2=<wbr>clang-tools-extra/trunk/clangd<wbr>/ClangdServer.cpp&p1=clang-<wbr>tools-extra/trunk/clangd/<wbr>ASTManager.cpp&r1=303060&r2=<wbr>303067&rev=303067&view=diff</a><br>==============================<wbr>==============================<wbr>==================<br>--- clang-tools-extra/trunk/clangd<wbr>/ASTManager.cpp (original)<br>+++ clang-tools-extra/trunk/clangd<wbr>/ClangdServer.cpp Mon May 15 09:17:35 2017<br>@@ -1,440 +1,149 @@<br>-//===--- ASTManager.cpp - Clang AST manager ------------------------------<wbr>-===//<br>+//===--- ClangdServer.cpp - Main clangd server code --------------*- C++-*-===//<br> //<br> // The LLVM Compiler Infrastructure<br> //<br> // This file is distributed under the University of Illinois Open Source<br> // License. See LICENSE.TXT for details.<br> //<br>-//===------------------------<wbr>------------------------------<wbr>----------------===//<br>+//===------------------------<wbr>------------------------------<wbr>-------------===//<br><br>-#include "ASTManager.h"<br>-#include "JSONRPCDispatcher.h"<br>-#include "Protocol.h"<br>+#include "ClangdServer.h"<br> #include "clang/Frontend/ASTUnit.h"<br> #include "clang/Frontend/CompilerInstan<wbr>ce.h"<br>+#include "clang/Frontend/CompilerInvoca<wbr>tion.h"<br> #include "clang/Tooling/CompilationData<wbr>base.h"<br>-#include "llvm/Support/Format.h"<br>-#include "llvm/Support/Path.h"<br>-#include <mutex><br>-#include <thread><br>-using namespace clang;<br>-using namespace clangd;<br>+#include "llvm/Support/FileSystem.h"<br><br>-void DocData::setAST(std::unique_pt<wbr>r<ASTUnit> AST) {<br>- this->AST = std::move(AST);<br>-}<br>-<br>-ASTUnit *DocData::getAST() const { return AST.get(); }<br>-<br>-void DocData::cacheFixIts(Diagnosti<wbr>cToReplacementMap FixIts) {<br>- this->FixIts = std::move(FixIts);<br>-}<br>+using namespace clang::clangd;<br><br>-std::vector<clang::tooling::R<wbr>eplacement><br>-DocData::getFixIts(const clangd::Diagnostic &D) const {<br>- auto it = FixIts.find(D);<br>- if (it != FixIts.end())<br>- return it->second;<br>- return {};<br>-}<br>-<br>-ASTManagerRequest::ASTManager<wbr>Request(ASTManagerRequestType Type,<br>- <wbr> std::string File,<br>- <wbr> DocVersion Version)<br>- : Type(Type), File(File), Version(Version) {}<br>-<br>-/// Retrieve a copy of the contents of every file in the store, for feeding into<br>-/// ASTUnit.<br>-static std::vector<ASTUnit::RemappedF<wbr>ile><br>-getRemappedFiles(const DocumentStore &Docs) {<br>- // FIXME: Use VFS instead. This would allow us to get rid of the chdir below.<br>- std::vector<ASTUnit::Remapped<wbr>File> RemappedFiles;<br>- for (const auto &P : Docs.getAllDocuments()) {<br>- StringRef FileName = P.first;<br>- RemappedFiles.push_back(AST<wbr>Unit::RemappedFile(<br>- FileName,<br>- llvm::MemoryBuffer::get<wbr>MemBufferCopy(P.second, FileName).release()));<br>- }<br>- return RemappedFiles;<br>-}<br>+WorkerRequest::WorkerRequest(<wbr>WorkerRequestKind Kind, Path File,<br>+ Do<wbr>cVersion Version)<br>+ : Kind(Kind), File(File), Version(Version) {}<br><br>-/// Convert from clang diagnostic level to LSP severity.<br>-static int getSeverity(DiagnosticsEngine:<wbr>:Level L) {<br>- switch (L) {<br>- case DiagnosticsEngine::Remark:<br>- return 4;<br>- case DiagnosticsEngine::Note:<br>- return 3;<br>- case DiagnosticsEngine::Warning:<br>- return 2;<br>- case DiagnosticsEngine::Fatal:<br>- case DiagnosticsEngine::Error:<br>- return 1;<br>- case DiagnosticsEngine::Ignored:<br>- return 0;<br>- }<br>- llvm_unreachable("Unknown diagnostic level!");<br>-}<br>-<br>-static CompletionItemKind getKind(CXCursorKind K) {<br>- switch (K) {<br>- case CXCursor_MacroInstantiation:<br>- case CXCursor_MacroDefinition:<br>- return CompletionItemKind::Text;<br>- case CXCursor_CXXMethod:<br>- return CompletionItemKind::Method;<br>- case CXCursor_FunctionDecl:<br>- case CXCursor_FunctionTemplate:<br>- return CompletionItemKind::Function;<br>- case CXCursor_Constructor:<br>- case CXCursor_Destructor:<br>- return CompletionItemKind::Constructo<wbr>r;<br>- case CXCursor_FieldDecl:<br>- return CompletionItemKind::Field;<br>- case CXCursor_VarDecl:<br>- case CXCursor_ParmDecl:<br>- return CompletionItemKind::Variable;<br>- case CXCursor_ClassDecl:<br>- case CXCursor_StructDecl:<br>- case CXCursor_UnionDecl:<br>- case CXCursor_ClassTemplate:<br>- case CXCursor_ClassTemplatePartialS<wbr>pecialization:<br>- return CompletionItemKind::Class;<br>- case CXCursor_Namespace:<br>- case CXCursor_NamespaceAlias:<br>- case CXCursor_NamespaceRef:<br>- return CompletionItemKind::Module;<br>- case CXCursor_EnumConstantDecl:<br>- return CompletionItemKind::Value;<br>- case CXCursor_EnumDecl:<br>- return CompletionItemKind::Enum;<br>- case CXCursor_TypeAliasDecl:<br>- case CXCursor_TypeAliasTemplateDecl<wbr>:<br>- case CXCursor_TypedefDecl:<br>- case CXCursor_MemberRef:<br>- case CXCursor_TypeRef:<br>- return CompletionItemKind::Reference;<br>- default:<br>- return CompletionItemKind::Missing;<br>- }<br>-}<br>-<br>-ASTManager::ASTManager(JSONOu<wbr>tput &Output, DocumentStore &Store,<br>- bool RunSynchronously)<br>- : Output(Output), Store(Store), RunSynchronously(RunSynchronou<wbr>sly),<br>- PCHs(std::make_shared<PCH<wbr>ContainerOperations>()),<br>- ClangWorker([this]() { runWorker(); }) {}<br>-<br>-void ASTManager::runWorker() {<br>- while (true) {<br>- ASTManagerRequest Request;<br>-<br>- // Pick request from the queue<br>- {<br>- std::unique_lock<std::mut<wbr>ex> Lock(RequestLock);<br>- // Wait for more requests.<br>- ClangRequestCV.wait(Lock,<br>- [this<wbr>] { return !RequestQueue.empty() || Done; });<br>- if (Done)<br>- return;<br>- assert(!RequestQueue.empt<wbr>y() && "RequestQueue was empty");<br>-<br>- Request = std::move(RequestQueue.back())<wbr>;<br>- RequestQueue.pop_back();<br>-<br>- // Skip outdated requests<br>- if (Request.Version != DocVersions.find(Request.File)<wbr>->second) {<br>- Output.log("Version for " + Twine(Request.File) +<br>- " in request is outdated, skipping request\n");<br>- continue;<br>- }<br>- } // unlock RequestLock<br>-<br>- handleRequest(Request.Type, Request.File);<br>- }<br>-}<br>-<br>-void ASTManager::queueOrRun(ASTMana<wbr>gerRequestType RequestType, StringRef File) {<br>+ClangdScheduler::ClangdSchedu<wbr>ler(ClangdServer &Server, bool RunSynchronously)<br>+ : RunSynchronously(RunSynchronou<wbr>sly) {<br> if (RunSynchronously) {<br>- handleRequest(RequestType, File);<br>+ // Don't start the worker thread if we're running synchronously<br> return;<br> }<br><br>- std::lock_guard<std::mutex> Guard(RequestLock);<br>- // We increment the version of the added document immediately and schedule<br>- // the requested operation to be run on a worker thread<br>- DocVersion version = ++DocVersions[File];<br>- RequestQueue.push_back(ASTMan<wbr>agerRequest(RequestType, File, version));<br>- ClangRequestCV.notify_one();<br>-}<br>-<br>-void ASTManager::handleRequest(ASTM<wbr>anagerRequestType RequestType,<br>- <wbr>StringRef File) {<br>- switch (RequestType) {<br>- case ASTManagerRequestType::ParseAn<wbr>dPublishDiagnostics:<br>- parseFileAndPublishDiagnost<wbr>ics(File);<br>- break;<br>- case ASTManagerRequestType::RemoveD<wbr>ocData: {<br>- std::lock_guard<std::mutex> Lock(ClangObjectLock);<br>- auto DocDataIt = DocDatas.find(File);<br>- // We could get the remove request before parsing for the document is<br>- // started, just do nothing in that case, parsing request will be discarded<br>- // because it has a lower version value<br>- if (DocDataIt == DocDatas.end())<br>- return;<br>- DocDatas.erase(DocDataIt);<br>- break;<br>- } // unlock ClangObjectLock<br>- }<br>-}<br>-<br>-void ASTManager::parseFileAndPublis<wbr>hDiagnostics(StringRef File) {<br>- std::unique_lock<std::mutex> ClangObjectLockGuard(ClangObje<wbr>ctLock);<br>-<br>- auto &DocData = DocDatas[File];<br>- ASTUnit *Unit = DocData.getAST();<br>- if (!Unit) {<br>- auto newAST = createASTUnitForFile(File, this->Store);<br>- Unit = newAST.get();<br>-<br>- DocData.setAST(std::move(ne<wbr>wAST));<br>- } else {<br>- // Do a reparse if this wasn't the first parse.<br>- // FIXME: This might have the wrong working directory if it changed in the<br>- // meantime.<br>- Unit->Reparse(PCHs, getRemappedFiles(this->Store))<wbr>;<br>- }<br>+ // Initialize Worker in ctor body, rather than init list to avoid potentially<br>+ // using not-yet-initialized members<br>+ Worker = std::thread([&Server, this]() {<br>+ while (true) {<br>+ WorkerRequest Request;<br>+<br>+ // Pick request from the queue<br>+ {<br>+ std::unique_lock<std::m<wbr>utex> Lock(Mutex);<br>+ // Wait for more requests.<br>+ RequestCV.wait(Lock, [this] { return !RequestQueue.empty() || Done; });<br>+ if (Done)<br>+ return;<br>+<br>+ assert(!RequestQueue.em<wbr>pty() && "RequestQueue was empty");<br>+<br>+ Request = std::move(RequestQueue.back())<wbr>;<br>+ RequestQueue.pop_back()<wbr>;<br>+<br>+ // Skip outdated requests<br>+ if (Request.Version != Server.DraftMgr.getVersion(Req<wbr>uest.File)) {<br>+ // FIXME(ibiryukov): Logging<br>+ // Output.log("Version for " + Twine(Request.File) +<br>+ // " in request is outdated, skipping request\n");<br>+ continue;<br>+ }<br>+ } // unlock Mutex<br><br>- if (!Unit)<br>- return;<br>-<br>- // Send the diagnotics to the editor.<br>- // FIXME: If the diagnostic comes from a different file, do we want to<br>- // show them all? Right now we drop everything not coming from the<br>- // main file.<br>- std::string Diagnostics;<br>- DocData::DiagnosticToReplacem<wbr>entMap LocalFixIts; // Temporary storage<br>- for (ASTUnit::stored_diag_iterator D = Unit->stored_diag_begin(),<br>- <wbr> DEnd = Unit->stored_diag_end();<br>- D != DEnd; ++D) {<br>- if (!D->getLocation().isValid() ||<br>- !D->getLocation().getMa<wbr>nager().isInMainFile(D->getLoc<wbr>ation()))<br>- continue;<br>- Position P;<br>- P.line = D->getLocation().getSpellingLi<wbr>neNumber() - 1;<br>- P.character = D->getLocation().getSpellingCo<wbr>lumnNumber();<br>- Range R = {P, P};<br>- Diagnostics +=<br>- R"({"range":)" + Range::unparse(R) +<br>- R"(,"severity":)" + std::to_string(getSeverity(D-><wbr>getLevel())) +<br>- R"(,"message":")" + llvm::yaml::escape(D->getMessa<wbr>ge()) +<br>- R"("},)";<br>-<br>- // We convert to Replacements to become independent of the SourceManager.<br>- clangd::Diagnostic Diag = {R, getSeverity(D->getLevel()), D->getMessage()};<br>- auto &FixItsForDiagnostic = LocalFixIts[Diag];<br>- for (const FixItHint &Fix : D->getFixIts()) {<br>- FixItsForDiagnostic.push_<wbr>back(clang::tooling::Replaceme<wbr>nt(<br>- Unit->getSourceManage<wbr>r(), Fix.RemoveRange, Fix.CodeToInsert));<br>+ Server.handleRequest(std:<wbr>:move(Request));<br> }<br>- }<br>-<br>- // Put FixIts into place.<br>- DocData.cacheFixIts(std::move<wbr>(LocalFixIts));<br>-<br>- ClangObjectLockGuard.unlock()<wbr>;<br>- // No accesses to clang objects are allowed after this point.<br>-<br>- // Publish diagnostics.<br>- if (!Diagnostics.empty())<br>- Diagnostics.pop_back(); // Drop trailing comma.<br>- Output.writeMessage(<br>- R"({"jsonrpc":"2.0","meth<wbr>od":"textDocument/publishDiagn<wbr>ostics","params":{"uri":")" +<br>- URI::fromFile(File).uri + R"(","diagnostics":[)" + Diagnostics + R"(]}})");<br>+ });<br> }<br><br>-ASTManager::~ASTManager() {<br>+ClangdScheduler::~ClangdSched<wbr>uler() {<br>+ if (RunSynchronously)<br>+ return; // no worker thread is running in that case<br>+<br> {<br>- std::lock_guard<std::mutex> Guard(RequestLock);<br>- // Wake up the clang worker thread, then exit.<br>+ std::lock_guard<std::mutex> Lock(Mutex);<br>+ // Wake up the worker thread<br> Done = true;<br>- ClangRequestCV.notify_one()<wbr>;<br>- } // unlock DocDataLock<br>- ClangWorker.join();<br>-}<br>-<br>-void ASTManager::onDocumentAdd(Stri<wbr>ngRef File) {<br>- queueOrRun(ASTManagerRequestT<wbr>ype::ParseAndPublishDiagnostic<wbr>s, File);<br>-}<br>-<br>-void ASTManager::onDocumentRemove(S<wbr>tringRef File) {<br>- queueOrRun(ASTManagerRequestT<wbr>ype::RemoveDocData, File);<br>-}<br>-<br>-tooling::CompilationDatabase *<br>-ASTManager::getOrCreateCompil<wbr>ationDatabaseForFile(StringRef File) {<br>- namespace path = llvm::sys::path;<br>-<br>- assert((path::is_absolute(Fil<wbr>e, path::Style::posix) ||<br>- path::is_absolute(Fil<wbr>e, path::Style::windows)) &&<br>- "path must be absolute");<br>-<br>- for (auto Path = path::parent_path(File); !Path.empty();<br>- Path = path::parent_path(Path)) {<br>-<br>- auto CachedIt = CompilationDatabases.find(Path<wbr>);<br>- if (CachedIt != CompilationDatabases.end())<br>- return CachedIt->second.get();<br>- std::string Error;<br>- auto CDB = tooling::CompilationDatabase::<wbr>loadFromDirectory(Path, Error);<br>- if (!CDB) {<br>- if (!Error.empty()) {<br>- Output.log("Error when trying to load compilation database from " +<br>- Twine(Path) + ": " + Twine(Error) + "\n");<br>- }<br>- continue;<br>- }<br>-<br>- // TODO(ibiryukov): Invalidate cached compilation databases on changes<br>- auto result = CDB.get();<br>- CompilationDatabases.insert<wbr>(std::make_pair(Path, std::move(CDB)));<br>- return result;<br>- }<br>-<br>- Output.log("Failed to find compilation database for " + Twine(File) + "\n");<br>- return nullptr;<br>+ RequestCV.notify_one();<br>+ } // unlock Mutex<br>+ Worker.join();<br> }<br><br>-std::unique_ptr<clang::ASTUni<wbr>t><br>-ASTManager::createASTUnitForF<wbr>ile(StringRef File, const DocumentStore &Docs) {<br>- tooling::CompilationDatabase *CDB =<br>- getOrCreateCompilationDat<wbr>abaseForFile(File);<br>-<br>- std::vector<tooling::CompileC<wbr>ommand> Commands;<br>-<br>- if (CDB) {<br>- Commands = CDB->getCompileCommands(File);<br>- // chdir. This is thread hostile.<br>- if (!Commands.empty())<br>- llvm::sys::fs::set_curren<wbr>t_path(Commands.front().Direct<wbr>ory);<br>- }<br>- if (Commands.empty()) {<br>- // Add a fake command line if we know nothing.<br>- Commands.push_back(tooling:<wbr>:CompileCommand(<br>- llvm::sys::path::parent<wbr>_path(File), llvm::sys::path::filename(File<wbr>),<br>- {"clang", "-fsyntax-only", File.str()}, ""));<br>+void ClangdScheduler::enqueue(Clang<wbr>dServer &Server, WorkerRequest Request) {<br>+ if (RunSynchronously) {<br>+ Server.handleRequest(Reques<wbr>t);<br>+ return;<br> }<br><br>- // Inject the resource dir.<br>- // FIXME: Don't overwrite it if it's already there.<br>- static int Dummy; // Just an address in this process.<br>- std::string ResourceDir =<br>- CompilerInvocation::GetRe<wbr>sourcesPath("clangd", (void *)&Dummy);<br>- Commands.front().CommandLine.<wbr>push_back("-resource-dir=" + ResourceDir);<br>-<br>- IntrusiveRefCntPtr<Diagnostic<wbr>sEngine> Diags =<br>- CompilerInstance::createD<wbr>iagnostics(new DiagnosticOptions);<br>-<br>- std::vector<const char *> ArgStrs;<br>- for (const auto &S : Commands.front().CommandLine)<br>- ArgStrs.push_back(S.c_str()<wbr>);<br>-<br>- auto ArgP = &*ArgStrs.begin();<br>- return std::unique_ptr<clang::ASTUnit<wbr>>(ASTUnit::LoadFromCommandLine<wbr>(<br>- ArgP, ArgP + ArgStrs.size(), PCHs, Diags, ResourceDir,<br>- /*OnlyLocalDecls=*/false, /*CaptureDiagnostics=*/true,<br>- getRemappedFiles(Docs),<br>- /*RemappedFilesKeepOrigin<wbr>alName=*/true,<br>- /*PrecompilePreambleAfter<wbr>NParses=*/1, /*TUKind=*/TU_Complete,<br>- /*CacheCodeCompletionResu<wbr>lts=*/true,<br>- /*IncludeBriefCommentsInC<wbr>odeCompletion=*/true,<br>- /*AllowPCHWithCompilerErr<wbr>ors=*/true));<br>+ std::lock_guard<std::mutex> Lock(Mutex);<br>+ RequestQueue.push_back(Reques<wbr>t);<br>+ RequestCV.notify_one();<br> }<br><br>-std::vector<clang::tooling::R<wbr>eplacement><br>-ASTManager::getFixIts(StringR<wbr>ef File, const clangd::Diagnostic &D) {<br>- // TODO(ibiryukov): the FixIts should be available immediately<br>- // even when parsing is being run on a worker thread<br>- std::lock_guard<std::mutex> Guard(ClangObjectLock);<br>- return DocDatas[File].getFixIts(D);<br>-}<br>+ClangdServer::ClangdServer(st<wbr>d::unique_ptr<GlobalCompilatio<wbr>nDatabase> CDB,<br>+ std:<wbr>:unique_ptr<DiagnosticsConsume<wbr>r> DiagConsumer,<br>+ bool RunSynchronously)<br>+ : CDB(std::move(CDB)), DiagConsumer(std::move(DiagCon<wbr>sumer)),<br>+ PCHs(std::make_shared<PCH<wbr>ContainerOperations>()),<br>+ WorkScheduler(*this, RunSynchronously) {}<br><br>-namespace {<br>-class CompletionItemsCollector : public CodeCompleteConsumer {<br>- std::vector<CompletionItem> *Items;<br>- std::shared_ptr<clang::Global<wbr>CodeCompletionAllocator> Allocator;<br>- CodeCompletionTUInfo CCTUInfo;<br>-<br>-public:<br>- CompletionItemsCollector(std:<wbr>:vector<CompletionItem> *Items,<br>- cons<wbr>t CodeCompleteOptions &CodeCompleteOpts)<br>- : CodeCompleteConsumer(CodeCompl<wbr>eteOpts, /*OutputIsBinary=*/false),<br>- Items(Items),<br>- Allocator(std::make_sha<wbr>red<clang::GlobalCodeCompletio<wbr>nAllocator>()),<br>- CCTUInfo(Allocator) {}<br>-<br>- void ProcessCodeCompleteResults(Sem<wbr>a &S, CodeCompletionContext Context,<br>- <wbr> CodeCompletionResult *Results,<br>- <wbr> unsigned NumResults) override {<br>- for (unsigned I = 0; I != NumResults; ++I) {<br>- CodeCompletionResult &Result = Results[I];<br>- CodeCompletionString *CCS = Result.CreateCodeCompletionStr<wbr>ing(<br>- S, Context, *Allocator, CCTUInfo,<br>- CodeCompleteOpts.Incl<wbr>udeBriefComments);<br>- if (CCS) {<br>- CompletionItem Item;<br>- assert(CCS->getTypedTex<wbr>t());<br>- Item.label = CCS->getTypedText();<br>- Item.kind = getKind(Result.CursorKind);<br>- if (CCS->getBriefComment())<br>- Item.documentation = CCS->getBriefComment();<br>- Items->push_back(std::m<wbr>ove(Item));<br>- }<br>- }<br>+void ClangdServer::addDocument(Path<wbr>Ref File, StringRef Contents) {<br>+ DocVersion NewVersion = DraftMgr.updateDraft(File, Contents);<br>+ WorkScheduler.enqueue(<br>+ *this, WorkerRequest(WorkerRequestKin<wbr>d::ParseAndPublishDiagnostics, File,<br>+ NewV<wbr>ersion));<br>+}<br>+<br>+void ClangdServer::removeDocument(P<wbr>athRef File) {<br>+ auto NewVersion = DraftMgr.removeDraft(File);<br>+ WorkScheduler.enqueue(<br>+ *this, WorkerRequest(WorkerRequestKin<wbr>d::RemoveDocData, File, NewVersion));<br>+}<br>+<br>+std::vector<CompletionItem> ClangdServer::codeComplete(Pat<wbr>hRef File,<br>+ <wbr> Positi<wbr>on Pos) {<br>+ auto FileContents = DraftMgr.getDraft(File);<br>+ assert(FileContents.Draft && "codeComplete is called for non-added document");<br>+<br>+ std::vector<CompletionItem> Result;<br>+ Units.runOnUnitWithoutReparse<wbr>(<br>+ File, *FileContents.Draft, *CDB, PCHs, [&](ClangdUnit &Unit) {<br>+ Result = Unit.codeComplete(*FileContent<wbr>s.Draft, Pos);<br>+ });<br>+ return Result;<br>+}<br>+<br>+std::string ClangdServer::getDocument(Path<wbr>Ref File) {<br>+ auto draft = DraftMgr.getDraft(File);<br>+ assert(draft.Draft && "File is not tracked, cannot get contents");<br>+ return *draft.Draft;<br>+}<br>+<br>+void ClangdServer::handleRequest(Wo<wbr>rkerRequest Request) {<br>+ switch (Request.Kind) {<br>+ case WorkerRequestKind::ParseAndPub<wbr>lishDiagnostics: {<br>+ auto FileContents = DraftMgr.getDraft(Request.File<wbr>);<br>+ if (FileContents.Version != Request.Version)<br>+ return; // This request is outdated, do nothing<br>+<br>+ assert(FileContents.Draft &&<br>+ "No contents inside a file that was scheduled for reparse");<br>+ Units.runOnUnit(Request.Fil<wbr>e, *FileContents.Draft, *CDB, PCHs,<br>+ [&](ClangdU<wbr>nit const &Unit) {<br>+ DiagConsu<wbr>mer->onDiagnosticsReady(<br>+ Reque<wbr>st.File, Unit.getLocalDiagnostics());<br>+ });<br>+ break;<br> }<br>+ case WorkerRequestKind::RemoveDocDa<wbr>ta:<br>+ if (Request.Version != DraftMgr.getVersion(Request.Fi<wbr>le))<br>+ return; // This request is outdated, do nothing<br><br>- GlobalCodeCompletionAllocator &getAllocator() override { return *Allocator; }<br>-<br>- CodeCompletionTUInfo &getCodeCompletionTUInfo() override { return CCTUInfo; }<br>-};<br>-<br>-} // namespace<br>-<br>-std::vector<CompletionItem><br>-ASTManager::codeComplete(Stri<wbr>ngRef File, unsigned Line, unsigned Column) {<br>- CodeCompleteOptions CCO;<br>- CCO.IncludeBriefComments = 1;<br>- // This is where code completion stores dirty buffers. Need to free after<br>- // completion.<br>- SmallVector<const llvm::MemoryBuffer *, 4> OwnedBuffers;<br>- SmallVector<StoredDiagnostic, 4> StoredDiagnostics;<br>- IntrusiveRefCntPtr<Diagnostic<wbr>sEngine> DiagEngine(<br>- new DiagnosticsEngine(new DiagnosticIDs, new DiagnosticOptions));<br>- std::vector<CompletionItem> Items;<br>- CompletionItemsCollector Collector(&Items, CCO);<br>-<br>- std::lock_guard<std::mutex> Guard(ClangObjectLock);<br>- auto &DocData = DocDatas[File];<br>- auto Unit = DocData.getAST();<br>- if (!Unit) {<br>- auto newAST = createASTUnitForFile(File, this->Store);<br>- Unit = newAST.get();<br>- DocData.setAST(std::move(ne<wbr>wAST));<br>+ Units.removeUnitIfPresent(R<wbr>equest.File);<br>+ break;<br> }<br>- if (!Unit)<br>- return {};<br>- IntrusiveRefCntPtr<SourceMana<wbr>ger> SourceMgr(<br>- new SourceManager(*DiagEngine, Unit->getFileManager()));<br>- // CodeComplete seems to require fresh LangOptions.<br>- LangOptions LangOpts = Unit->getLangOpts();<br>- // The language server protocol uses zero-based line and column numbers.<br>- // The clang code completion uses one-based numbers.<br>- Unit->CodeComplete(File, Line + 1, Column + 1, getRemappedFiles(this->Store),<br>- CCO.Includ<wbr>eMacros, CCO.IncludeCodePatterns,<br>- CCO.Includ<wbr>eBriefComments, Collector, PCHs, *DiagEngine,<br>- LangOpts, *SourceMgr, Unit->getFileManager(),<br>- StoredDiag<wbr>nostics, OwnedBuffers);<br>- for (const llvm::MemoryBuffer *Buffer : OwnedBuffers)<br>- delete Buffer;<br>- return Items;<br> }<br><br>Copied: clang-tools-extra/trunk/clangd<wbr>/ClangdServer.h (from r303063, clang-tools-extra/trunk/clangd<wbr>/ASTManager.h)<br>URL: <a href="http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/ClangdServer.h?p2=clang-tools-extra/trunk/clangd/ClangdServer.h&p1=clang-tools-extra/trunk/clangd/ASTManager.h&r1=303063&r2=303067&rev=303067&view=diff" target="_blank">http://llvm.org/viewvc/llvm-pr<wbr>oject/clang-tools-extra/trunk/<wbr>clangd/ClangdServer.h?p2=<wbr>clang-tools-extra/trunk/clangd<wbr>/ClangdServer.h&p1=clang-<wbr>tools-extra/trunk/clangd/<wbr>ASTManager.h&r1=303063&r2=<wbr>303067&rev=303067&view=diff</a><br>==============================<wbr>==============================<wbr>==================<br>--- clang-tools-extra/trunk/clangd<wbr>/ASTManager.h (original)<br>+++ clang-tools-extra/trunk/clangd<wbr>/ClangdServer.h Mon May 15 09:17:35 2017<br>@@ -1,4 +1,4 @@<br>-//===--- ASTManager.h - Clang AST manager -----------------------*- C++ -*-===//<br>+//===--- ClangdServer.h - Main clangd server code ----------------*- C++-*-===//<br> //<br> // The LLVM Compiler Infrastructure<br> //<br>@@ -7,153 +7,129 @@<br> //<br> //===-------------------------<wbr>------------------------------<wbr>---------------===//<br><br>-#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_<wbr>ASTMANAGER_H<br>-#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_<wbr>ASTMANAGER_H<br>+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_<wbr>CLANGDSERVER_H<br>+#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_<wbr>CLANGDSERVER_H<br><br>-#include "DocumentStore.h"<br>-#include "JSONRPCDispatcher.h"<br>-#include "Protocol.h"<br>+#include "ClangdUnitStore.h"<br>+#include "DraftStore.h"<br>+#include "GlobalCompilationDatabase.h"<br>+#include "clang/Frontend/ASTUnit.h"<br>+#include "clang/Tooling/CompilationData<wbr>base.h"<br> #include "clang/Tooling/Core/Replacemen<wbr>t.h"<br> #include "llvm/ADT/IntrusiveRefCntPtr.h<wbr>"<br>+#include "llvm/ADT/Optional.h"<br>+#include "llvm/ADT/StringRef.h"<br>+<br>+#include "ClangdUnit.h"<br>+#include "Protocol.h"<br>+<br> #include <condition_variable><br>-#include <deque><br>+#include <mutex><br>+#include <string><br> #include <thread><br>+#include <utility><br><br> namespace clang {<br>-class ASTUnit;<br>-class DiagnosticsEngine;<br> class PCHContainerOperations;<br>-namespace tooling {<br>-class CompilationDatabase;<br>-} // namespace tooling<br><br> namespace clangd {<br><br>-/// Using 'unsigned' here to avoid undefined behaviour on overflow.<br>-typedef unsigned DocVersion;<br>-<br>-/// Stores ASTUnit and FixIts map for an opened document.<br>-class DocData {<br>-public:<br>- typedef std::map<clangd::Diagnostic, std::vector<clang::tooling::Re<wbr>placement>><br>- DiagnosticToReplacementMa<wbr>p;<br>-<br>+class DiagnosticsConsumer {<br> public:<br>- void setAST(std::unique_ptr<ASTUnit<wbr>> AST);<br>- ASTUnit *getAST() const;<br>-<br>- void cacheFixIts(DiagnosticToReplac<wbr>ementMap FixIts);<br>- std::vector<clang::tooling::R<wbr>eplacement><br>- getFixIts(const clangd::Diagnostic &D) const;<br>+ virtual ~DiagnosticsConsumer() = default;<br><br>-private:<br>- std::unique_ptr<ASTUnit> AST;<br>- DiagnosticToReplacementMap FixIts;<br>+ /// Called by ClangdServer when \p Diagnostics for \p File are ready.<br>+ virtual void onDiagnosticsReady(PathRef File,<br>+ <wbr> std::vector<DiagWithFixIts> Diagnostics) = 0;<br> };<br><br>-enum class ASTManagerRequestType { ParseAndPublishDiagnostics, RemoveDocData };<br>+enum class WorkerRequestKind { ParseAndPublishDiagnostics, RemoveDocData };<br><br> /// A request to the worker thread<br>-class ASTManagerRequest {<br>+class WorkerRequest {<br> public:<br>- ASTManagerRequest() = default;<br>- ASTManagerRequest(ASTManagerR<wbr>equestType Type, std::string File,<br>- DocVersion Version);<br>+ WorkerRequest() = default;<br>+ WorkerRequest(WorkerRequestKi<wbr>nd Kind, Path File, DocVersion Version);<br><br>- ASTManagerRequestType Type;<br>- std::string File;<br>+ WorkerRequestKind Kind;<br>+ Path File;<br> DocVersion Version;<br> };<br><br>-class ASTManager : public DocumentStoreListener {<br>-public:<br>- ASTManager(JSONOutput &Output, DocumentStore &Store, bool RunSynchronously);<br>- ~ASTManager() override;<br>-<br>- void onDocumentAdd(StringRef File) override;<br>- void onDocumentRemove(StringRef File) override;<br>+class ClangdServer;<br><br>- /// Get code completions at a specified \p Line and \p Column in \p File.<br>- ///<br>- /// This function is thread-safe and returns completion items that own the<br>- /// data they contain.<br>- std::vector<CompletionItem> codeComplete(StringRef File, unsigned Line,<br>- <wbr> unsigned Column);<br>-<br>- /// Get the fixes associated with a certain diagnostic in a specified file as<br>- /// replacements.<br>- ///<br>- /// This function is thread-safe. It returns a copy to avoid handing out<br>- /// references to unguarded data.<br>- std::vector<clang::tooling::R<wbr>eplacement><br>- getFixIts(StringRef File, const clangd::Diagnostic &D);<br>+/// Handles running WorkerRequests of ClangdServer on a separate threads.<br>+/// Currently runs only one worker thread.<br>+class ClangdScheduler {<br>+public:<br>+ ClangdScheduler(ClangdServer &Server, bool RunSynchronously);<br>+ ~ClangdScheduler();<br><br>- DocumentStore &getStore() const { return Store; }<br>+ /// Enqueue WorkerRequest to be run on a worker thread<br>+ void enqueue(ClangdServer &Server, WorkerRequest Request);<br><br> private:<br>- JSONOutput &Output;<br>- DocumentStore &Store;<br>-<br>- // Set to true if requests should block instead of being processed<br>- // asynchronously.<br> bool RunSynchronously;<br>-<br>- /// Loads a compilation database for File. May return nullptr if it fails. The<br>- /// database is cached for subsequent accesses.<br>- clang::tooling::CompilationDa<wbr>tabase *<br>- getOrCreateCompilationDatabas<wbr>eForFile(StringRef File);<br>- // Creates a new ASTUnit for the document at File.<br>- // FIXME: This calls chdir internally, which is thread unsafe.<br>- std::unique_ptr<clang::ASTUni<wbr>t><br>- createASTUnitForFile(StringRe<wbr>f File, const DocumentStore &Docs);<br>-<br>- /// If RunSynchronously is false, queues the request to be run on the worker<br>- /// thread.<br>- /// If RunSynchronously is true, runs the request handler immediately on the<br>- /// main thread.<br>- void queueOrRun(ASTManagerRequestTy<wbr>pe RequestType, StringRef File);<br>-<br>- void runWorker();<br>- void handleRequest(ASTManagerReques<wbr>tType RequestType, StringRef File);<br>-<br>- /// Parses files and publishes diagnostics.<br>- /// This function is called on the worker thread in asynchronous mode and<br>- /// on the main thread in synchronous mode.<br>- void parseFileAndPublishDiagnostics<wbr>(StringRef File);<br>-<br>- /// Caches compilation databases loaded from directories(keys are directories).<br>- llvm::StringMap<std::unique_p<wbr>tr<clang::tooling::Compilation<wbr>Database>><br>- CompilationDatabases;<br>-<br>- /// Clang objects.<br>- /// A map from filenames to DocData structures that store ASTUnit and Fixits for<br>- /// the files. The ASTUnits are used for generating diagnostics and fix-it-s<br>- /// asynchronously by the worker thread and synchronously for code completion.<br>- llvm::StringMap<DocData> DocDatas;<br>- std::shared_ptr<clang::PCHCon<wbr>tainerOperations> PCHs;<br>- /// A lock for access to the DocDatas, CompilationDatabases and PCHs.<br>- std::mutex ClangObjectLock;<br>-<br>- /// Stores latest versions of the tracked documents to discard outdated requests.<br>- /// Guarded by RequestLock.<br>- /// TODO(ibiryukov): the entries are neved deleted from this map.<br>- llvm::StringMap<DocVersion> DocVersions;<br>-<br>- /// A LIFO queue of requests. Note that requests are discarded if the `version`<br>- /// field is not equal to the one stored inside DocVersions.<br>- /// TODO(krasimir): code completion should always have priority over parsing<br>- /// for diagnostics.<br>- std::deque<ASTManagerRequest> RequestQueue;<br>+ std::mutex Mutex;<br>+ /// We run some tasks on a separate thread(parsing, ClangdUnit cleanup).<br>+ /// This thread looks into RequestQueue to find requests to handle and<br>+ /// terminates when Done is set to true.<br>+ std::thread Worker;<br> /// Setting Done to true will make the worker thread terminate.<br> bool Done = false;<br>+ /// A LIFO queue of requests. Note that requests are discarded if the<br>+ /// `version` field is not equal to the one stored inside DraftStore.<br>+ /// FIXME(krasimir): code completion should always have priority over parsing<br>+ /// for diagnostics.<br>+ std::deque<WorkerRequest> RequestQueue;<br> /// Condition variable to wake up the worker thread.<br>- std::condition_variable ClangRequestCV;<br>- /// Lock for accesses to RequestQueue, DocVersions and Done.<br>- std::mutex RequestLock;<br>-<br>- /// We run parsing on a separate thread. This thread looks into RequestQueue to<br>- /// find requests to handle and terminates when Done is set to true.<br>- std::thread ClangWorker;<br>+ std::condition_variable RequestCV;<br>+};<br>+<br>+/// Provides API to manage ASTs for a collection of C++ files and request<br>+/// various language features(currently, only codeCompletion and async<br>+/// diagnostics for tracked files).<br>+class ClangdServer {<br>+public:<br>+ ClangdServer(std::unique_ptr<<wbr>GlobalCompilationDatabase> CDB,<br>+ std::unique_ptr<<wbr>DiagnosticsConsumer> DiagConsumer,<br>+ bool RunSynchronously);<br>+<br>+ /// Add a \p File to the list of tracked C++ files or update the contents if<br>+ /// \p File is already tracked. Also schedules parsing of the AST for it on a<br>+ /// separate thread. When the parsing is complete, DiagConsumer passed in<br>+ /// constructor will receive onDiagnosticsReady callback.<br>+ void addDocument(PathRef File, StringRef Contents);<br>+<br>+ /// Remove \p File from list of tracked files, schedule a request to free<br>+ /// resources associated with it.<br>+ void removeDocument(PathRef File);<br>+<br>+ /// Run code completion for \p File at \p Pos.<br>+ std::vector<CompletionItem> codeComplete(PathRef File, Position Pos);<br>+<br>+ /// Gets current document contents for \p File. \p File must point to a<br>+ /// currently tracked file.<br>+ /// FIXME(ibiryukov): This function is here to allow implementation of<br>+ /// formatCode from ProtocolHandlers.cpp. We should move formatCode to this<br>+ /// class and remove this function from public interface.<br>+ std::string getDocument(PathRef File);<br>+<br>+private:<br>+ friend class ClangdScheduler;<br>+<br>+ /// This function is called on a worker thread.<br>+ void handleRequest(WorkerRequest Request);<br>+<br>+ std::unique_ptr<GlobalCompila<wbr>tionDatabase> CDB;<br>+ std::unique_ptr<DiagnosticsCo<wbr>nsumer> DiagConsumer;<br>+ DraftStore DraftMgr;<br>+ ClangdUnitStore Units;<br>+ std::shared_ptr<PCHContainerO<wbr>perations> PCHs;<br>+ // WorkScheduler has to be the last member, because its destructor has to be<br>+ // called before all other members to stop the worker thread that references<br>+ // ClangdServer<br>+ ClangdScheduler WorkScheduler;<br> };<br><br> } // namespace clangd<br><br>Added: clang-tools-extra/trunk/clangd<wbr>/ClangdUnit.cpp<br>URL: <a href="http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/ClangdUnit.cpp?rev=303067&view=auto" target="_blank">http://llvm.org/viewvc/llvm-pr<wbr>oject/clang-tools-extra/trunk/<wbr>clangd/ClangdUnit.cpp?rev=<wbr>303067&view=auto</a><br>==============================<wbr>==============================<wbr>==================<br>--- clang-tools-extra/trunk/clangd<wbr>/ClangdUnit.cpp (added)<br>+++ clang-tools-extra/trunk/clangd<wbr>/ClangdUnit.cpp Mon May 15 09:17:35 2017<br>@@ -0,0 +1,224 @@<br>+//===--- ClangdUnit.cpp ------------------------------<wbr>-----------*- C++-*-===//<br>+//<br>+// The LLVM Compiler Infrastructure<br>+//<br>+// This file is distributed under the University of Illinois Open Source<br>+// License. See LICENSE.TXT for details.<br>+//<br>+//===------------------------<wbr>------------------------------<wbr>---------------===//<br>+<br>+#include "ClangdUnit.h"<br>+#include "clang/Frontend/ASTUnit.h"<br>+#include "clang/Frontend/CompilerInstan<wbr>ce.h"<br>+#include "clang/Frontend/CompilerInvoca<wbr>tion.h"<br>+#include "clang/Tooling/CompilationData<wbr>base.h"<br>+<br>+using namespace clang::clangd;<br>+using namespace clang;<br>+<br>+ClangdUnit::ClangdUnit(PathRe<wbr>f FileName, StringRef Contents,<br>+ std::sha<wbr>red_ptr<PCHContainerOperations<wbr>> PCHs,<br>+ std::vec<wbr>tor<tooling::CompileCommand> Commands)<br>+ : FileName(FileName), PCHs(PCHs) {<br>+ assert(!Commands.empty() && "No compile commands provided");<br>+<br>+ // Inject the resource dir.<br>+ // FIXME: Don't overwrite it if it's already there.<br>+ static int Dummy; // Just an address in this process.<br>+ std::string ResourceDir =<br>+ CompilerInvocation::GetRe<wbr>sourcesPath("clangd", (void *)&Dummy);<br>+ Commands.front().CommandLine.<wbr>push_back("-resource-dir=" + ResourceDir);<br>+<br>+ IntrusiveRefCntPtr<Diagnostic<wbr>sEngine> Diags =<br>+ CompilerInstance::createD<wbr>iagnostics(new DiagnosticOptions);<br>+<br>+ std::vector<const char *> ArgStrs;<br>+ for (const auto &S : Commands.front().CommandLine)<br>+ ArgStrs.push_back(S.c_str()<wbr>);<br>+<br>+ ASTUnit::RemappedFile RemappedSource(<br>+ FileName,<br>+ llvm::MemoryBuffer::getMe<wbr>mBufferCopy(Contents, FileName).release());<br>+<br>+ auto ArgP = &*ArgStrs.begin();<br>+ Unit = std::unique_ptr<ASTUnit>(ASTUn<wbr>it::LoadFromCommandLine(<br>+ ArgP, ArgP + ArgStrs.size(), PCHs, Diags, ResourceDir,<br>+ /*OnlyLocalDecls=*/false, /*CaptureDiagnostics=*/true, RemappedSource,<br>+ /*RemappedFilesKeepOrigin<wbr>alName=*/true,<br>+ /*PrecompilePreambleAfter<wbr>NParses=*/1, /*TUKind=*/TU_Complete,<br>+ /*CacheCodeCompletionResu<wbr>lts=*/true,<br>+ /*IncludeBriefCommentsInC<wbr>odeCompletion=*/true,<br>+ /*AllowPCHWithCompilerErr<wbr>ors=*/true));<br>+}<br>+<br>+void ClangdUnit::reparse(StringRef Contents) {<br>+ // Do a reparse if this wasn't the first parse.<br>+ // FIXME: This might have the wrong working directory if it changed in the<br>+ // meantime.<br>+ ASTUnit::RemappedFile RemappedSource(<br>+ FileName,<br>+ llvm::MemoryBuffer::getMe<wbr>mBufferCopy(Contents, FileName).release());<br>+<br>+ Unit->Reparse(PCHs, RemappedSource);<br>+}<br>+<br>+namespace {<br>+<br>+CompletionItemKind getKind(CXCursorKind K) {<br>+ switch (K) {<br>+ case CXCursor_MacroInstantiation:<br>+ case CXCursor_MacroDefinition:<br>+ return CompletionItemKind::Text;<br>+ case CXCursor_CXXMethod:<br>+ return CompletionItemKind::Method;<br>+ case CXCursor_FunctionDecl:<br>+ case CXCursor_FunctionTemplate:<br>+ return CompletionItemKind::Function;<br>+ case CXCursor_Constructor:<br>+ case CXCursor_Destructor:<br>+ return CompletionItemKind::Constructo<wbr>r;<br>+ case CXCursor_FieldDecl:<br>+ return CompletionItemKind::Field;<br>+ case CXCursor_VarDecl:<br>+ case CXCursor_ParmDecl:<br>+ return CompletionItemKind::Variable;<br>+ case CXCursor_ClassDecl:<br>+ case CXCursor_StructDecl:<br>+ case CXCursor_UnionDecl:<br>+ case CXCursor_ClassTemplate:<br>+ case CXCursor_ClassTemplatePartialS<wbr>pecialization:<br>+ return CompletionItemKind::Class;<br>+ case CXCursor_Namespace:<br>+ case CXCursor_NamespaceAlias:<br>+ case CXCursor_NamespaceRef:<br>+ return CompletionItemKind::Module;<br>+ case CXCursor_EnumConstantDecl:<br>+ return CompletionItemKind::Value;<br>+ case CXCursor_EnumDecl:<br>+ return CompletionItemKind::Enum;<br>+ case CXCursor_TypeAliasDecl:<br>+ case CXCursor_TypeAliasTemplateDecl<wbr>:<br>+ case CXCursor_TypedefDecl:<br>+ case CXCursor_MemberRef:<br>+ case CXCursor_TypeRef:<br>+ return CompletionItemKind::Reference;<br>+ default:<br>+ return CompletionItemKind::Missing;<br>+ }<br>+}<br>+<br>+class CompletionItemsCollector : public CodeCompleteConsumer {<br>+ std::vector<CompletionItem> *Items;<br>+ std::shared_ptr<clang::Global<wbr>CodeCompletionAllocator> Allocator;<br>+ CodeCompletionTUInfo CCTUInfo;<br>+<br>+public:<br>+ CompletionItemsCollector(std:<wbr>:vector<CompletionItem> *Items,<br>+ cons<wbr>t CodeCompleteOptions &CodeCompleteOpts)<br>+ : CodeCompleteConsumer(CodeCompl<wbr>eteOpts, /*OutputIsBinary=*/false),<br>+ Items(Items),<br>+ Allocator(std::make_sha<wbr>red<clang::GlobalCodeCompletio<wbr>nAllocator>()),<br>+ CCTUInfo(Allocator) {}<br>+<br>+ void ProcessCodeCompleteResults(Sem<wbr>a &S, CodeCompletionContext Context,<br>+ <wbr> CodeCompletionResult *Results,<br>+ <wbr> unsigned NumResults) override {<br>+ for (unsigned I = 0; I != NumResults; ++I) {<br>+ CodeCompletionResult &Result = Results[I];<br>+ CodeCompletionString *CCS = Result.CreateCodeCompletionStr<wbr>ing(<br>+ S, Context, *Allocator, CCTUInfo,<br>+ CodeCompleteOpts.Incl<wbr>udeBriefComments);<br>+ if (CCS) {<br>+ CompletionItem Item;<br>+ assert(CCS->getTypedTex<wbr>t());<br>+ Item.label = CCS->getTypedText();<br>+ Item.kind = getKind(Result.CursorKind);<br>+ if (CCS->getBriefComment())<br>+ Item.documentation = CCS->getBriefComment();<br>+ Items->push_back(std::m<wbr>ove(Item));<br>+ }<br>+ }<br>+ }<br>+<br>+ GlobalCodeCompletionAllocator &getAllocator() override { return *Allocator; }<br>+<br>+ CodeCompletionTUInfo &getCodeCompletionTUInfo() override { return CCTUInfo; }<br>+};<br>+} // namespace<br>+<br>+std::vector<CompletionItem> ClangdUnit::codeComplete(Strin<wbr>gRef Contents,<br>+ <wbr> Position Pos) {<br>+ CodeCompleteOptions CCO;<br>+ CCO.IncludeBriefComments = 1;<br>+ // This is where code completion stores dirty buffers. Need to free after<br>+ // completion.<br>+ SmallVector<const llvm::MemoryBuffer *, 4> OwnedBuffers;<br>+ SmallVector<StoredDiagnostic, 4> StoredDiagnostics;<br>+ IntrusiveRefCntPtr<Diagnostic<wbr>sEngine> DiagEngine(<br>+ new DiagnosticsEngine(new DiagnosticIDs, new DiagnosticOptions));<br>+ std::vector<CompletionItem> Items;<br>+ CompletionItemsCollector Collector(&Items, CCO);<br>+<br>+ ASTUnit::RemappedFile RemappedSource(<br>+ FileName,<br>+ llvm::MemoryBuffer::getMe<wbr>mBufferCopy(Contents, FileName).release());<br>+<br>+ IntrusiveRefCntPtr<SourceMana<wbr>ger> SourceMgr(<br>+ new SourceManager(*DiagEngine, Unit->getFileManager()));<br>+ // CodeComplete seems to require fresh LangOptions.<br>+ LangOptions LangOpts = Unit->getLangOpts();<br>+ // The language server protocol uses zero-based line and column numbers.<br>+ // The clang code completion uses one-based numbers.<br>+ Unit->CodeComplete(FileName, Pos.line + 1, Pos.character + 1, RemappedSource,<br>+ CCO.Includ<wbr>eMacros, CCO.IncludeCodePatterns,<br>+ CCO.Includ<wbr>eBriefComments, Collector, PCHs, *DiagEngine,<br>+ LangOpts, *SourceMgr, Unit->getFileManager(),<br>+ StoredDiag<wbr>nostics, OwnedBuffers);<br>+ for (const llvm::MemoryBuffer *Buffer : OwnedBuffers)<br>+ delete Buffer;<br>+ return Items;<br>+}<br>+<br>+namespace {<br>+/// Convert from clang diagnostic level to LSP severity.<br>+static int getSeverity(DiagnosticsEngine:<wbr>:Level L) {<br>+ switch (L) {<br>+ case DiagnosticsEngine::Remark:<br>+ return 4;<br>+ case DiagnosticsEngine::Note:<br>+ return 3;<br>+ case DiagnosticsEngine::Warning:<br>+ return 2;<br>+ case DiagnosticsEngine::Fatal:<br>+ case DiagnosticsEngine::Error:<br>+ return 1;<br>+ case DiagnosticsEngine::Ignored:<br>+ return 0;<br>+ }<br>+ llvm_unreachable("Unknown diagnostic level!");<br>+}<br>+} // namespace<br>+<br>+std::vector<DiagWithFixIts> ClangdUnit::getLocalDiagnostic<wbr>s() const {<br>+ std::vector<DiagWithFixIts> Result;<br>+ for (ASTUnit::stored_diag_iterator D = Unit->stored_diag_begin(),<br>+ <wbr> DEnd = Unit->stored_diag_end();<br>+ D != DEnd; ++D) {<br>+ if (!D->getLocation().isValid() ||<br>+ !D->getLocation().getMa<wbr>nager().isInMainFile(D->getLoc<wbr>ation()))<br>+ continue;<br>+ Position P;<br>+ P.line = D->getLocation().getSpellingLi<wbr>neNumber() - 1;<br>+ P.character = D->getLocation().getSpellingCo<wbr>lumnNumber();<br>+ Range R = {P, P};<br>+ clangd::Diagnostic Diag = {R, getSeverity(D->getLevel()), D->getMessage()};<br>+<br>+ llvm::SmallVector<tooling::<wbr>Replacement, 1> FixItsForDiagnostic;<br>+ for (const FixItHint &Fix : D->getFixIts()) {<br>+ FixItsForDiagnostic.push_<wbr>back(clang::tooling::Replaceme<wbr>nt(<br>+ Unit->getSourceManage<wbr>r(), Fix.RemoveRange, Fix.CodeToInsert));<br>+ }<br>+ Result.push_back({Diag, std::move(FixItsForDiagnostic)<wbr>});<br>+ }<br>+ return Result;<br>+}<br><br>Added: clang-tools-extra/trunk/clangd<wbr>/ClangdUnit.h<br>URL: <a href="http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/ClangdUnit.h?rev=303067&view=auto" target="_blank">http://llvm.org/viewvc/llvm-pr<wbr>oject/clang-tools-extra/trunk/<wbr>clangd/ClangdUnit.h?rev=303067<wbr>&view=auto</a><br>==============================<wbr>==============================<wbr>==================<br>--- clang-tools-extra/trunk/clangd<wbr>/ClangdUnit.h (added)<br>+++ clang-tools-extra/trunk/clangd<wbr>/ClangdUnit.h Mon May 15 09:17:35 2017<br>@@ -0,0 +1,63 @@<br>+//===--- ClangdUnit.h ------------------------------<wbr>-------------*- C++-*-===//<br>+//<br>+// The LLVM Compiler Infrastructure<br>+//<br>+// This file is distributed under the University of Illinois Open Source<br>+// License. See LICENSE.TXT for details.<br>+//<br>+//===------------------------<wbr>------------------------------<wbr>---------------===//<br>+<br>+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_<wbr>CLANGDUNIT_H<br>+#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_<wbr>CLANGDUNIT_H<br>+<br>+#include "Protocol.h"<br>+#include "Path.h"<br>+#include "clang/Frontend/ASTUnit.h"<br>+#include "clang/Tooling/Core/Replacemen<wbr>t.h"<br>+#include <memory><br>+<br>+namespace clang {<br>+class ASTUnit;<br>+class PCHContainerOperations;<br>+<br>+namespace tooling {<br>+struct CompileCommand;<br>+}<br>+<br>+namespace clangd {<br>+<br>+/// A diagnostic with its FixIts.<br>+struct DiagWithFixIts {<br>+ clangd::Diagnostic Diag;<br>+ llvm::SmallVector<tooling::Re<wbr>placement, 1> FixIts;<br>+};<br>+<br>+/// Stores parsed C++ AST and provides implementations of all operations clangd<br>+/// would want to perform on parsed C++ files.<br>+class ClangdUnit {<br>+public:<br>+ ClangdUnit(PathRef FileName, StringRef Contents,<br>+ std::shared_ptr<PC<wbr>HContainerOperations> PCHs,<br>+ std::vector<toolin<wbr>g::CompileCommand> Commands);<br>+<br>+ /// Reparse with new contents.<br>+ void reparse(StringRef Contents);<br>+<br>+ /// Get code completions at a specified \p Line and \p Column in \p File.<br>+ ///<br>+ /// This function is thread-safe and returns completion items that own the<br>+ /// data they contain.<br>+ std::vector<CompletionItem> codeComplete(StringRef Contents, Position Pos);<br>+ /// Returns diagnostics and corresponding FixIts for each diagnostic that are<br>+ /// located in the current file.<br>+ std::vector<DiagWithFixIts> getLocalDiagnostics() const;<br>+<br>+private:<br>+ Path FileName;<br>+ std::unique_ptr<ASTUnit> Unit;<br>+ std::shared_ptr<PCHContainerO<wbr>perations> PCHs;<br>+};<br>+<br>+} // namespace clangd<br>+} // namespace clang<br>+#endif<br><br>Added: clang-tools-extra/trunk/clangd<wbr>/ClangdUnitStore.cpp<br>URL: <a href="http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/ClangdUnitStore.cpp?rev=303067&view=auto" target="_blank">http://llvm.org/viewvc/llvm-pr<wbr>oject/clang-tools-extra/trunk/<wbr>clangd/ClangdUnitStore.cpp?<wbr>rev=303067&view=auto</a><br>==============================<wbr>==============================<wbr>==================<br>--- clang-tools-extra/trunk/clangd<wbr>/ClangdUnitStore.cpp (added)<br>+++ clang-tools-extra/trunk/clangd<wbr>/ClangdUnitStore.cpp Mon May 15 09:17:35 2017<br>@@ -0,0 +1,34 @@<br>+//===--- ClangdUnitStore.cpp - A ClangdUnits container -----------*-C++-*-===//<br>+//<br>+// The LLVM Compiler Infrastructure<br>+//<br>+// This file is distributed under the University of Illinois Open Source<br>+// License. See LICENSE.TXT for details.<br>+//<br>+//===------------------------<wbr>------------------------------<wbr>----------------===//<br>+<br>+#include "ClangdUnitStore.h"<br>+#include "llvm/Support/Path.h"<br>+<br>+using namespace clang::clangd;<br>+using namespace clang;<br>+<br>+void ClangdUnitStore::removeUnitIfP<wbr>resent(PathRef File) {<br>+ std::lock_guard<std::mutex> Lock(Mutex);<br>+<br>+ auto It = OpenedFiles.find(File);<br>+ if (It == OpenedFiles.end())<br>+ return;<br>+ OpenedFiles.erase(It);<br>+}<br>+<br>+std::vector<tooling::CompileC<wbr>ommand> ClangdUnitStore::getCompileCom<wbr>mands(GlobalCompilationDatabas<wbr>e &CDB, PathRef File) {<br>+ std::vector<tooling::CompileC<wbr>ommand> Commands = CDB.getCompileCommands(File);<br>+ if (Commands.empty()) {<br>+ // Add a fake command line if we know nothing.<br>+ Commands.push_back(tooling:<wbr>:CompileCommand(<br>+ llvm::sys::path::parent<wbr>_path(File), llvm::sys::path::filename(File<wbr>),<br>+ {"clang", "-fsyntax-only", File.str()}, ""));<br>+ }<br>+ return Commands;<br>+}<br><br>Added: clang-tools-extra/trunk/clangd<wbr>/ClangdUnitStore.h<br>URL: <a href="http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/ClangdUnitStore.h?rev=303067&view=auto" target="_blank">http://llvm.org/viewvc/llvm-pr<wbr>oject/clang-tools-extra/trunk/<wbr>clangd/ClangdUnitStore.h?rev=<wbr>303067&view=auto</a><br>==============================<wbr>==============================<wbr>==================<br>--- clang-tools-extra/trunk/clangd<wbr>/ClangdUnitStore.h (added)<br>+++ clang-tools-extra/trunk/clangd<wbr>/ClangdUnitStore.h Mon May 15 09:17:35 2017<br>@@ -0,0 +1,93 @@<br>+//===--- ClangdUnitStore.h - A ClangdUnits container -------------*-C++-*-===//<br>+//<br>+// The LLVM Compiler Infrastructure<br>+//<br>+// This file is distributed under the University of Illinois Open Source<br>+// License. See LICENSE.TXT for details.<br>+//<br>+//===------------------------<wbr>------------------------------<wbr>---------------===//<br>+<br>+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_<wbr>CLANGDUNITSTORE_H<br>+#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_<wbr>CLANGDUNITSTORE_H<br>+<br>+#include <mutex><br>+<br>+#include "ClangdUnit.h"<br>+#include "GlobalCompilationDatabase.h"<br>+#include "Path.h"<br>+#include "clang/Tooling/CompilationData<wbr>base.h"<br>+<br>+namespace clang {<br>+namespace clangd {<br>+<br>+/// Thread-safe collection of ASTs built for specific files. Provides<br>+/// synchronized access to ASTs.<br>+class ClangdUnitStore {<br>+public:<br>+ /// Run specified \p Action on the ClangdUnit for \p File.<br>+ /// If the file is not present in ClangdUnitStore, a new ClangdUnit will be<br>+ /// created from the \p FileContents. If the file is already present in the<br>+ /// store, ClangdUnit::reparse will be called with the new contents before<br>+ /// running \p Action.<br>+ template <class Func><br>+ void runOnUnit(PathRef File, StringRef FileContents,<br>+ GlobalCompilat<wbr>ionDatabase &CDB,<br>+ std::shared_pt<wbr>r<PCHContainerOperations> PCHs, Func Action) {<br>+ runOnUnitImpl(File, FileContents, CDB, PCHs, /*ReparseBeforeAction=*/true,<br>+ std::forward<<wbr>Func>(Action));<br>+ }<br>+<br>+ /// Run specified \p Action on the ClangdUnit for \p File.<br>+ /// If the file is not present in ClangdUnitStore, a new ClangdUnit will be<br>+ /// created from the \p FileContents. If the file is already present in the<br>+ /// store, the \p Action will be run directly on it.<br>+ template <class Func><br>+ void runOnUnitWithoutReparse(PathRe<wbr>f File, StringRef FileContents,<br>+ <wbr>GlobalCompilationDatabase &CDB,<br>+ <wbr>std::shared_ptr<PCHContainerOp<wbr>erations> PCHs,<br>+ <wbr>Func Action) {<br>+ runOnUnitImpl(File, FileContents, CDB, PCHs, /*ReparseBeforeAction=*/false,<br>+ std::forward<<wbr>Func>(Action));<br>+ }<br>+<br>+ /// Remove ClangdUnit for \p File, if any<br>+ void removeUnitIfPresent(PathRef File);<br>+<br>+private:<br>+ /// Run specified \p Action on the ClangdUnit for \p File.<br>+ template <class Func><br>+ void runOnUnitImpl(PathRef File, StringRef FileContents,<br>+ GlobalComp<wbr>ilationDatabase &CDB,<br>+ std::share<wbr>d_ptr<PCHContainerOperations> PCHs,<br>+ bool ReparseBeforeAction, Func Action) {<br>+ std::lock_guard<std::mutex> Lock(Mutex);<br>+<br>+ auto Commands = getCompileCommands(CDB, File);<br>+ assert(!Commands.empty() &&<br>+ "getCompileCommands should add default command");<br>+ // chdir. This is thread hostile.<br>+ // FIXME(ibiryukov): get rid of this<br>+ llvm::sys::fs::set_current_<wbr>path(Commands.front().Director<wbr>y);<br>+<br>+ auto It = OpenedFiles.find(File);<br>+ if (It == OpenedFiles.end()) {<br>+ It = OpenedFiles<br>+ .insert(std::mak<wbr>e_pair(<br>+ File, ClangdUnit(File, FileContents, PCHs, Commands)))<br>+ .first;<br>+ } else if (ReparseBeforeAction) {<br>+ It->second.reparse(FileCo<wbr>ntents);<br>+ }<br>+ return Action(It->second);<br>+ }<br>+<br>+ std::vector<tooling::CompileC<wbr>ommand><br>+ getCompileCommands(GlobalComp<wbr>ilationDatabase &CDB, PathRef File);<br>+<br>+ std::mutex Mutex;<br>+ llvm::StringMap<ClangdUnit> OpenedFiles;<br>+};<br>+} // namespace clangd<br>+} // namespace clang<br>+<br>+#endif<br><br>Removed: clang-tools-extra/trunk/clangd<wbr>/DocumentStore.h<br>URL: <a href="http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/DocumentStore.h?rev=303066&view=auto" target="_blank">http://llvm.org/viewvc/llvm-pr<wbr>oject/clang-tools-extra/trunk/<wbr>clangd/DocumentStore.h?rev=<wbr>303066&view=auto</a><br>==============================<wbr>==============================<wbr>==================<br>--- clang-tools-extra/trunk/clangd<wbr>/DocumentStore.h (original)<br>+++ clang-tools-extra/trunk/clangd<wbr>/DocumentStore.h (removed)<br>@@ -1,86 +0,0 @@<br>-//===--- DocumentStore.h - File contents container --------------*- C++ -*-===//<br>-//<br>-// The LLVM Compiler Infrastructure<br>-//<br>-// This file is distributed under the University of Illinois Open Source<br>-// License. See LICENSE.TXT for details.<br>-//<br>-//===------------------------<wbr>------------------------------<wbr>----------------===//<br>-<br>-#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_<wbr>DOCUMENTSTORE_H<br>-#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_<wbr>DOCUMENTSTORE_H<br>-<br>-#include "clang/Basic/LLVM.h"<br>-#include "llvm/ADT/StringMap.h"<br>-#include <mutex><br>-#include <string><br>-#include <vector><br>-<br>-namespace clang {<br>-namespace clangd {<br>-class DocumentStore;<br>-<br>-struct DocumentStoreListener {<br>- virtual ~DocumentStoreListener() = default;<br>- virtual void onDocumentAdd(StringRef File) {}<br>- virtual void onDocumentRemove(StringRef File) {}<br>-};<br>-<br>-/// A container for files opened in a workspace, addressed by File. The contents<br>-/// are owned by the DocumentStore.<br>-class DocumentStore {<br>-public:<br>- /// Add a document to the store. Overwrites existing contents.<br>- void addDocument(StringRef File, StringRef Text) {<br>- {<br>- std::lock_guard<std::mute<wbr>x> Guard(DocsMutex);<br>- Docs[File] = Text;<br>- }<br>- for (const auto &Listener : Listeners)<br>- Listener->onDocumentAdd(F<wbr>ile);<br>- }<br>- /// Delete a document from the store.<br>- void removeDocument(StringRef File) {<br>- {<br>- std::lock_guard<std::mute<wbr>x> Guard(DocsMutex);<br>- Docs.erase(File);<br>- }<br>- for (const auto &Listener : Listeners)<br>- Listener->onDocumentRemov<wbr>e(File);<br>- }<br>- /// Retrieve a document from the store. Empty string if it's unknown.<br>- ///<br>- /// This function is thread-safe. It returns a copy to avoid handing out<br>- /// references to unguarded data.<br>- std::string getDocument(StringRef File) const {<br>- // FIXME: This could be a reader lock.<br>- std::lock_guard<std::mutex> Guard(DocsMutex);<br>- return Docs.lookup(File);<br>- }<br>-<br>- /// Add a listener. Does not take ownership.<br>- void addListener(DocumentStoreListe<wbr>ner *DSL) { Listeners.push_back(DSL); }<br>-<br>- /// Get name and constents of all documents in this store.<br>- ///<br>- /// This function is thread-safe. It returns a copies to avoid handing out<br>- /// references to unguarded data.<br>- std::vector<std::pair<std::st<wbr>ring, std::string>> getAllDocuments() const {<br>- std::vector<std::pair<std::<wbr>string, std::string>> AllDocs;<br>- std::lock_guard<std::mutex> Guard(DocsMutex);<br>- for (const auto &P : Docs)<br>- AllDocs.emplace_back(P.fi<wbr>rst(), P.second);<br>- return AllDocs;<br>- }<br>-<br>-private:<br>- llvm::StringMap<std::string> Docs;<br>- std::vector<DocumentStoreList<wbr>ener *> Listeners;<br>-<br>- mutable std::mutex DocsMutex;<br>-};<br>-<br>-} // namespace clangd<br>-} // namespace clang<br>-<br>-#endif<br><br>Added: clang-tools-extra/trunk/clangd<wbr>/DraftStore.cpp<br>URL: <a href="http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/DraftStore.cpp?rev=303067&view=auto" target="_blank">http://llvm.org/viewvc/llvm-pr<wbr>oject/clang-tools-extra/trunk/<wbr>clangd/DraftStore.cpp?rev=<wbr>303067&view=auto</a><br>==============================<wbr>==============================<wbr>==================<br>--- clang-tools-extra/trunk/clangd<wbr>/DraftStore.cpp (added)<br>+++ clang-tools-extra/trunk/clangd<wbr>/DraftStore.cpp Mon May 15 09:17:35 2017<br>@@ -0,0 +1,48 @@<br>+//===--- DraftStore.cpp - File contents container ---------------*- C++ -*-===//<br>+//<br>+// The LLVM Compiler Infrastructure<br>+//<br>+// This file is distributed under the University of Illinois Open Source<br>+// License. See LICENSE.TXT for details.<br>+//<br>+//===------------------------<wbr>------------------------------<wbr>----------------===//<br>+<br>+#include "DraftStore.h"<br>+<br>+using namespace clang::clangd;<br>+<br>+VersionedDraft DraftStore::getDraft(PathRef File) const {<br>+ std::lock_guard<std::mutex> Lock(Mutex);<br>+<br>+ auto It = Drafts.find(File);<br>+ if (It == Drafts.end())<br>+ return {0, llvm::None};<br>+ return It->second;<br>+}<br>+<br>+DocVersion DraftStore::getVersion(PathRef File) const {<br>+ std::lock_guard<std::mutex> Lock(Mutex);<br>+<br>+ auto It = Drafts.find(File);<br>+ if (It == Drafts.end())<br>+ return 0;<br>+ return It->second.Version;<br>+}<br>+<br>+DocVersion DraftStore::updateDraft(PathRe<wbr>f File, StringRef Contents) {<br>+ std::lock_guard<std::mutex> Lock(Mutex);<br>+<br>+ auto &Entry = Drafts[File];<br>+ DocVersion NewVersion = ++Entry.Version;<br>+ Entry.Draft = Contents;<br>+ return NewVersion;<br>+}<br>+<br>+DocVersion DraftStore::removeDraft(PathRe<wbr>f File) {<br>+ std::lock_guard<std::mutex> Lock(Mutex);<br>+<br>+ auto &Entry = Drafts[File];<br>+ DocVersion NewVersion = ++Entry.Version;<br>+ Entry.Draft = llvm::None;<br>+ return NewVersion;<br>+}<br><br>Copied: clang-tools-extra/trunk/clangd<wbr>/DraftStore.h (from r303060, clang-tools-extra/trunk/clangd<wbr>/DocumentStore.h)<br>URL: <a href="http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/DraftStore.h?p2=clang-tools-extra/trunk/clangd/DraftStore.h&p1=clang-tools-extra/trunk/clangd/DocumentStore.h&r1=303060&r2=303067&rev=303067&view=diff" target="_blank">http://llvm.org/viewvc/llvm-pr<wbr>oject/clang-tools-extra/trunk/<wbr>clangd/DraftStore.h?p2=clang-<wbr>tools-extra/trunk/clangd/<wbr>DraftStore.h&p1=clang-tools-<wbr>extra/trunk/clangd/DocumentSto<wbr>re.h&r1=303060&r2=303067&rev=<wbr>303067&view=diff</a><br>==============================<wbr>==============================<wbr>==================<br>--- clang-tools-extra/trunk/clangd<wbr>/DocumentStore.h (original)<br>+++ clang-tools-extra/trunk/clangd<wbr>/DraftStore.h Mon May 15 09:17:35 2017<br>@@ -1,4 +1,4 @@<br>-//===--- DocumentStore.h - File contents container --------------*- C++ -*-===//<br>+//===--- DraftStore.h - File contents container -----------------*- C++ -*-===//<br> //<br> // The LLVM Compiler Infrastructure<br> //<br>@@ -7,9 +7,10 @@<br> //<br> //===-------------------------<wbr>------------------------------<wbr>---------------===//<br><br>-#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_<wbr>DOCUMENTSTORE_H<br>-#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_<wbr>DOCUMENTSTORE_H<br>+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_<wbr>DRAFTSTORE_H<br>+#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_<wbr>DRAFTSTORE_H<br><br>+#include "Path.h"<br> #include "clang/Basic/LLVM.h"<br> #include "llvm/ADT/StringMap.h"<br> #include <mutex><br>@@ -18,66 +19,40 @@<br><br> namespace clang {<br> namespace clangd {<br>-class DocumentStore;<br><br>-struct DocumentStoreListener {<br>- virtual ~DocumentStoreListener() = default;<br>- virtual void onDocumentAdd(StringRef File) {}<br>- virtual void onDocumentRemove(StringRef File) {}<br>+/// Using 'unsigned' here to avoid undefined behaviour on overflow.<br>+typedef unsigned DocVersion;<br>+<br>+/// Document draft with a version of this draft.<br>+struct VersionedDraft {<br>+ DocVersion Version;<br>+ /// If the value of the field is None, draft is now deleted<br>+ llvm::Optional<std::string> Draft;<br> };<br><br>-/// A container for files opened in a workspace, addressed by File. The contents<br>-/// are owned by the DocumentStore.<br>-class DocumentStore {<br>+/// A thread-safe container for files opened in a workspace, addressed by<br>+/// filenames. The contents are owned by the DraftStore. Versions are mantained<br>+/// for the all added documents, including removed ones. The document version is<br>+/// incremented on each update and removal of the document.<br>+class DraftStore {<br> public:<br>- /// Add a document to the store. Overwrites existing contents.<br>- void addDocument(StringRef File, StringRef Text) {<br>- {<br>- std::lock_guard<std::mute<wbr>x> Guard(DocsMutex);<br>- Docs[File] = Text;<br>- }<br>- for (const auto &Listener : Listeners)<br>- Listener->onDocumentAdd(F<wbr>ile);<br>- }<br>- /// Delete a document from the store.<br>- void removeDocument(StringRef File) {<br>- {<br>- std::lock_guard<std::mute<wbr>x> Guard(DocsMutex);<br>- Docs.erase(File);<br>- }<br>- for (const auto &Listener : Listeners)<br>- Listener->onDocumentRemov<wbr>e(File);<br>- }<br>- /// Retrieve a document from the store. Empty string if it's unknown.<br>- ///<br>- /// This function is thread-safe. It returns a copy to avoid handing out<br>- /// references to unguarded data.<br>- std::string getDocument(StringRef File) const {<br>- // FIXME: This could be a reader lock.<br>- std::lock_guard<std::mutex> Guard(DocsMutex);<br>- return Docs.lookup(File);<br>- }<br>-<br>- /// Add a listener. Does not take ownership.<br>- void addListener(DocumentStoreListe<wbr>ner *DSL) { Listeners.push_back(DSL); }<br>-<br>- /// Get name and constents of all documents in this store.<br>- ///<br>- /// This function is thread-safe. It returns a copies to avoid handing out<br>- /// references to unguarded data.<br>- std::vector<std::pair<std::st<wbr>ring, std::string>> getAllDocuments() const {<br>- std::vector<std::pair<std::<wbr>string, std::string>> AllDocs;<br>- std::lock_guard<std::mutex> Guard(DocsMutex);<br>- for (const auto &P : Docs)<br>- AllDocs.emplace_back(P.fi<wbr>rst(), P.second);<br>- return AllDocs;<br>- }<br>+ /// \return version and contents of the stored document.<br>+ /// For untracked files, a (0, None) pair is returned.<br>+ VersionedDraft getDraft(PathRef File) const;<br>+ /// \return version of the tracked document.<br>+ /// For untracked files, 0 is returned.<br>+ DocVersion getVersion(PathRef File) const;<br>+<br>+ /// Replace contents of the draft for \p File with \p Contents.<br>+ /// \return The new version of the draft for \p File.<br>+ DocVersion updateDraft(PathRef File, StringRef Contents);<br>+ /// Remove the contents of the draft<br>+ /// \return The new version of the draft for \p File.<br>+ DocVersion removeDraft(PathRef File);<br><br> private:<br>- llvm::StringMap<std::string> Docs;<br>- std::vector<DocumentStoreList<wbr>ener *> Listeners;<br>-<br>- mutable std::mutex DocsMutex;<br>+ mutable std::mutex Mutex;<br>+ llvm::StringMap<VersionedDraf<wbr>t> Drafts;<br> };<br><br> } // namespace clangd<br><br>Added: clang-tools-extra/trunk/clangd<wbr>/GlobalCompilationDatabase.cpp<br>URL: <a href="http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/GlobalCompilationDatabase.cpp?rev=303067&view=auto" target="_blank">http://llvm.org/viewvc/llvm-pr<wbr>oject/clang-tools-extra/trunk/<wbr>clangd/GlobalCompilationDataba<wbr>se.cpp?rev=303067&view=auto</a><br>==============================<wbr>==============================<wbr>==================<br>--- clang-tools-extra/trunk/clangd<wbr>/GlobalCompilationDatabase.cpp (added)<br>+++ clang-tools-extra/trunk/clangd<wbr>/GlobalCompilationDatabase.cpp Mon May 15 09:17:35 2017<br>@@ -0,0 +1,65 @@<br>+//===--- GlobalCompilationDatabase.cpp --------------------------*- C++-*-===//<br>+//<br>+// The LLVM Compiler Infrastructure<br>+//<br>+// This file is distributed under the University of Illinois Open Source<br>+// License. See LICENSE.TXT for details.<br>+//<br>+//===------------------------<wbr>------------------------------<wbr>---------------===//<br>+<br>+#include "GlobalCompilationDatabase.h"<br>+#include "clang/Tooling/CompilationData<wbr>base.h"<br>+#include "llvm/Support/FileSystem.h"<br>+#include "llvm/Support/Path.h"<br>+<br>+using namespace clang::clangd;<br>+using namespace clang;<br>+<br>+std::vector<tooling::CompileC<wbr>ommand><br>+DirectoryBasedGlobalCompilati<wbr>onDatabase::<wbr>getCompileCommands(PathRef File) {<br>+ std::vector<tooling::CompileC<wbr>ommand> Commands;<br>+<br>+ auto CDB = getCompilationDatabase(File);<br>+ if (!CDB)<br>+ return {};<br>+ return CDB->getCompileCommands(File);<br>+}<br>+<br>+tooling::CompilationDatabase *<br>+DirectoryBasedGlobalCompilati<wbr>onDatabase::getCompilationData<wbr>base(PathRef File) {<br>+ std::lock_guard<std::mutex> Lock(Mutex);<br>+<br>+ namespace path = llvm::sys::path;<br>+<br>+ assert((path::is_absolute(Fil<wbr>e, path::Style::posix) ||<br>+ path::is_absolute(Fil<wbr>e, path::Style::windows)) &&<br>+ "path must be absolute");<br>+<br>+ for (auto Path = path::parent_path(File); !Path.empty();<br>+ Path = path::parent_path(Path)) {<br>+<br>+ auto CachedIt = CompilationDatabases.find(Path<wbr>);<br>+ if (CachedIt != CompilationDatabases.end())<br>+ return CachedIt->second.get();<br>+ std::string Error;<br>+ auto CDB = tooling::CompilationDatabase::<wbr>loadFromDirectory(Path, Error);<br>+ if (!CDB) {<br>+ if (!Error.empty()) {<br>+ // FIXME(ibiryukov): logging<br>+ // Output.log("Error when trying to load compilation database from " +<br>+ // Twine(Path) + ": " + Twine(Error) + "\n");<br>+ }<br>+ continue;<br>+ }<br>+<br>+ // FIXME(ibiryukov): Invalidate cached compilation databases on changes<br>+ auto result = CDB.get();<br>+ CompilationDatabases.insert<wbr>(std::make_pair(Path, std::move(CDB)));<br>+ return result;<br>+ }<br>+<br>+ // FIXME(ibiryukov): logging<br>+ // Output.log("Failed to find compilation database for " + Twine(File) +<br>+ // "\n");<br>+ return nullptr;<br>+}<br><br>Added: clang-tools-extra/trunk/clangd<wbr>/GlobalCompilationDatabase.h<br>URL: <a href="http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/GlobalCompilationDatabase.h?rev=303067&view=auto" target="_blank">http://llvm.org/viewvc/llvm-pr<wbr>oject/clang-tools-extra/trunk/<wbr>clangd/GlobalCompilationDataba<wbr>se.h?rev=303067&view=auto</a><br>==============================<wbr>==============================<wbr>==================<br>--- clang-tools-extra/trunk/clangd<wbr>/GlobalCompilationDatabase.h (added)<br>+++ clang-tools-extra/trunk/clangd<wbr>/GlobalCompilationDatabase.h Mon May 15 09:17:35 2017<br>@@ -0,0 +1,59 @@<br>+//===--- GlobalCompilationDatabase.h ----------------------------*- C++-*-===//<br>+//<br>+// The LLVM Compiler Infrastructure<br>+//<br>+// This file is distributed under the University of Illinois Open Source<br>+// License. See LICENSE.TXT for details.<br>+//<br>+//===------------------------<wbr>------------------------------<wbr>---------------===//<br>+<br>+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_<wbr>GLOBALCOMPILATIONDATABASE_H<br>+#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_<wbr>GLOBALCOMPILATIONDATABASE_H<br>+<br>+#include "Path.h"<br>+#include "llvm/ADT/StringMap.h"<br>+#include <memory><br>+#include <mutex><br>+<br>+namespace clang {<br>+<br>+namespace tooling {<br>+class CompilationDatabase;<br>+struct CompileCommand;<br>+} // namespace tooling<br>+<br>+namespace clangd {<br>+<br>+/// Provides compilation arguments used for building ClangdUnit.<br>+class GlobalCompilationDatabase {<br>+public:<br>+ virtual ~GlobalCompilationDatabase() = default;<br>+<br>+ virtual std::vector<tooling::CompileCo<wbr>mmand><br>+ getCompileCommands(PathRef File) = 0;<br>+<br>+ /// FIXME(ibiryukov): add facilities to track changes to compilation flags of<br>+ /// existing targets.<br>+};<br>+<br>+/// Gets compile args from tooling::CompilationDatabases built for parent<br>+/// directories.<br>+class DirectoryBasedGlobalCompilatio<wbr>nDatabase<br>+ : public GlobalCompilationDatabase {<br>+public:<br>+ std::vector<tooling::CompileC<wbr>ommand><br>+ getCompileCommands(PathRef File) override;<br>+<br>+private:<br>+ tooling::CompilationDatabase *getCompilationDatabase(PathRe<wbr>f File);<br>+<br>+ std::mutex Mutex;<br>+ /// Caches compilation databases loaded from directories(keys are<br>+ /// directories).<br>+ llvm::StringMap<std::unique_p<wbr>tr<clang::tooling::Compilation<wbr>Database>><br>+ CompilationDatabases;<br>+};<br>+} // namespace clangd<br>+} // namespace clang<br>+<br>+#endif<br><br>Added: clang-tools-extra/trunk/clangd<wbr>/Path.h<br>URL: <a href="http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/Path.h?rev=303067&view=auto" target="_blank">http://llvm.org/viewvc/llvm-pr<wbr>oject/clang-tools-extra/trunk/<wbr>clangd/Path.h?rev=303067&view=<wbr>auto</a><br>==============================<wbr>==============================<wbr>==================<br>--- clang-tools-extra/trunk/clangd<wbr>/Path.h (added)<br>+++ clang-tools-extra/trunk/clangd<wbr>/Path.h Mon May 15 09:17:35 2017<br>@@ -0,0 +1,29 @@<br>+//===--- Path.h - Helper typedefs ------------------------------<wbr>--*- C++-*-===//<br>+//<br>+// The LLVM Compiler Infrastructure<br>+//<br>+// This file is distributed under the University of Illinois Open Source<br>+// License. See LICENSE.TXT for details.<br>+//<br>+//===------------------------<wbr>------------------------------<wbr>----------------===//<br>+<br>+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_<wbr>PATH_H<br>+#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_<wbr>PATH_H<br>+<br>+#include "llvm/ADT/StringRef.h"<br>+#include <string><br>+<br>+namespace clang {<br>+namespace clangd {<br>+<br>+/// A typedef to represent a file path. Used solely for more descriptive<br>+/// signatures.<br>+using Path = std::string;<br>+/// A typedef to represent a ref to file path. Used solely for more descriptive<br>+/// signatures.<br>+using PathRef = llvm::StringRef;<br>+<br>+} // namespace clangd<br>+} // namespace clang<br>+<br>+#endif<br><br>Modified: clang-tools-extra/trunk/clangd<wbr>/ProtocolHandlers.cpp<br>URL: <a href="http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/ProtocolHandlers.cpp?rev=303067&r1=303066&r2=303067&view=diff" target="_blank">http://llvm.org/viewvc/llvm-pr<wbr>oject/clang-tools-extra/trunk/<wbr>clangd/ProtocolHandlers.cpp?<wbr>rev=303067&r1=303066&r2=303067<wbr>&view=diff</a><br>==============================<wbr>==============================<wbr>==================<br>--- clang-tools-extra/trunk/clangd<wbr>/ProtocolHandlers.cpp (original)<br>+++ clang-tools-extra/trunk/clangd<wbr>/ProtocolHandlers.cpp Mon May 15 09:17:35 2017<br>@@ -8,9 +8,10 @@<br> //===-------------------------<wbr>------------------------------<wbr>---------------===//<br><br> #include "ProtocolHandlers.h"<br>-#include "ASTManager.h"<br>-#include "DocumentStore.h"<br>+#include "ClangdServer.h"<br>+#include "DraftStore.h"<br> #include "clang/Format/Format.h"<br>+#include "ClangdLSPServer.h"<br> using namespace clang;<br> using namespace clangd;<br><br>@@ -21,7 +22,7 @@ void TextDocumentDidOpenHandler::ha<wbr>ndleN<br> Output.log("Failed to decode DidOpenTextDocumentParams!\n")<wbr>;<br> return;<br> }<br>- Store.addDocument(DOTDP->text<wbr>Document.uri.file, DOTDP->textDocument.text);<br>+ AST.openDocument(DOTDP->textD<wbr>ocument.uri.file, DOTDP->textDocument.text);<br> }<br><br> void TextDocumentDidCloseHandler::h<wbr>andleNotification(<br>@@ -32,7 +33,7 @@ void TextDocumentDidCloseHandler::h<wbr>andle<br> return;<br> }<br><br>- Store.removeDocument(DCTDP->t<wbr>extDocument.uri.file);<br>+ AST.closeDocument(DCTDP->text<wbr>Document.uri.file);<br> }<br><br> void TextDocumentDidChangeHandler::<wbr>handleNotification(<br>@@ -43,7 +44,7 @@ void TextDocumentDidChangeHandler::<wbr>handl<br> return;<br> }<br> // We only support full syncing right now.<br>- Store.addDocument(DCTDP->text<wbr>Document.uri.file, DCTDP->contentChanges[0].text)<wbr>;<br>+ AST.openDocument(DCTDP->textD<wbr>ocument.uri.file, DCTDP->contentChanges[0].text)<wbr>;<br> }<br><br> /// Turn a [line, column] pair into an offset in Code.<br>@@ -110,7 +111,7 @@ void TextDocumentRangeFormattingHan<wbr>dler:<br> return;<br> }<br><br>- std::string Code = Store.getDocument(DRFP->textDo<wbr>cument.uri.file);<br>+ std::string Code = AST.getDocument(DRFP->textDocu<wbr>ment.uri.file);<br><br> size_t Begin = positionToOffset(Code, DRFP->range.start);<br> size_t Len = positionToOffset(Code, DRFP->range.end) - Begin;<br>@@ -129,7 +130,7 @@ void TextDocumentOnTypeFormattingHa<wbr>ndler<br><br> // Look for the previous opening brace from the character position and format<br> // starting from there.<br>- std::string Code = Store.getDocument(DOTFP->textD<wbr>ocument.uri.file);<br>+ std::string Code = AST.getDocument(DOTFP->textDoc<wbr>ument.uri.file);<br> size_t CursorPos = positionToOffset(Code, DOTFP->position);<br> size_t PreviousLBracePos = StringRef(Code).find_last_of('<wbr>{', CursorPos);<br> if (PreviousLBracePos == StringRef::npos)<br>@@ -149,7 +150,7 @@ void TextDocumentFormattingHandler:<wbr>:hand<br> }<br><br> // Format everything.<br>- std::string Code = Store.getDocument(DFP->textDoc<wbr>ument.uri.file);<br>+ std::string Code = AST.getDocument(DFP->textDocum<wbr>ent.uri.file);<br> writeMessage(formatCode(Code<wbr>, DFP->textDocument.uri.file,<br> {cla<wbr>ng::tooling::Range(0, Code.size())}, ID));<br> }<br>@@ -164,7 +165,7 @@ void CodeActionHandler::handleMetho<wbr>d(llv<br><br> // We provide a code action for each diagnostic at the requested location<br> // which has FixIts available.<br>- std::string Code = AST.getStore().getDocument(CAP<wbr>->textDocument.uri.file);<br>+ std::string Code = AST.getDocument(CAP->textDocum<wbr>ent.uri.file);<br> std::string Commands;<br> for (Diagnostic &D : CAP->context.diagnostics) {<br> std::vector<clang::tooling<wbr>::Replacement> Fixes = AST.getFixIts(CAP->textDocumen<wbr>t.uri.file, D);<br>@@ -195,8 +196,8 @@ void CompletionHandler::handleMetho<wbr>d(llv<br> return;<br> }<br><br>- auto Items = AST.codeComplete(TDPP->textDoc<wbr>ument.uri.file, TDPP->position.line,<br>- <wbr> TDPP->position.character);<br>+ auto Items = AST.codeComplete(TDPP->textDoc<wbr>ument.uri.file, Position{TDPP->position.line,<br>+ TDPP->position.charac<wbr>ter});<br> std::string Completions;<br> for (const auto &Item : Items) {<br> Completions += CompletionItem::unparse(Item);<br><br>Modified: clang-tools-extra/trunk/clangd<wbr>/ProtocolHandlers.h<br>URL: <a href="http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/ProtocolHandlers.h?rev=303067&r1=303066&r2=303067&view=diff" target="_blank">http://llvm.org/viewvc/llvm-pr<wbr>oject/clang-tools-extra/trunk/<wbr>clangd/ProtocolHandlers.h?rev=<wbr>303067&r1=303066&r2=303067&<wbr>view=diff</a><br>==============================<wbr>==============================<wbr>==================<br>--- clang-tools-extra/trunk/clangd<wbr>/ProtocolHandlers.h (original)<br>+++ clang-tools-extra/trunk/clangd<wbr>/ProtocolHandlers.h Mon May 15 09:17:35 2017<br>@@ -22,8 +22,8 @@<br><br> namespace clang {<br> namespace clangd {<br>-class ASTManager;<br>-class DocumentStore;<br>+class ClangdLSPServer;<br>+class ClangdLSPServer;<br><br> struct InitializeHandler : Handler {<br> InitializeHandler(JSONOutput &Output) : Handler(Output) {}<br>@@ -56,83 +56,83 @@ private:<br> };<br><br> struct TextDocumentDidOpenHandler : Handler {<br>- TextDocumentDidOpenHandler(JS<wbr>ONOutput &Output, DocumentStore &Store)<br>- : Handler(Output), Store(Store) {}<br>+ TextDocumentDidOpenHandler(JS<wbr>ONOutput &Output, ClangdLSPServer &AST)<br>+ : Handler(Output), AST(AST) {}<br><br> void handleNotification(llvm::yaml:<wbr>:MappingNode *Params) override;<br><br> private:<br>- DocumentStore &Store;<br>+ ClangdLSPServer &AST;<br> };<br><br> struct TextDocumentDidChangeHandler : Handler {<br>- TextDocumentDidChangeHandler(<wbr>JSONOutput &Output, DocumentStore &Store)<br>- : Handler(Output), Store(Store) {}<br>+ TextDocumentDidChangeHandler(<wbr>JSONOutput &Output, ClangdLSPServer &AST)<br>+ : Handler(Output), AST(AST) {}<br><br> void handleNotification(llvm::yaml:<wbr>:MappingNode *Params) override;<br><br> private:<br>- DocumentStore &Store;<br>+ ClangdLSPServer &AST;<br> };<br><br> struct TextDocumentDidCloseHandler : Handler {<br>- TextDocumentDidCloseHandler(J<wbr>SONOutput &Output, DocumentStore &Store)<br>- : Handler(Output), Store(Store) {}<br>+ TextDocumentDidCloseHandler(J<wbr>SONOutput &Output, ClangdLSPServer &AST)<br>+ : Handler(Output), AST(AST) {}<br><br> void handleNotification(llvm::yaml:<wbr>:MappingNode *Params) override;<br><br> private:<br>- DocumentStore &Store;<br>+ ClangdLSPServer &AST;<br> };<br><br> struct TextDocumentOnTypeFormattingHa<wbr>ndler : Handler {<br>- TextDocumentOnTypeFormattingH<wbr>andler(JSONOutput &Output, DocumentStore &Store)<br>- : Handler(Output), Store(Store) {}<br>+ TextDocumentOnTypeFormattingH<wbr>andler(JSONOutput &Output, ClangdLSPServer &AST)<br>+ : Handler(Output), AST(AST) {}<br><br> void handleMethod(llvm::yaml::Mappi<wbr>ngNode *Params, StringRef ID) override;<br><br> private:<br>- DocumentStore &Store;<br>+ ClangdLSPServer &AST;<br> };<br><br> struct TextDocumentRangeFormattingHan<wbr>dler : Handler {<br>- TextDocumentRangeFormattingHa<wbr>ndler(JSONOutput &Output, DocumentStore &Store)<br>- : Handler(Output), Store(Store) {}<br>+ TextDocumentRangeFormattingHa<wbr>ndler(JSONOutput &Output, ClangdLSPServer &AST)<br>+ : Handler(Output), AST(AST) {}<br><br> void handleMethod(llvm::yaml::Mappi<wbr>ngNode *Params, StringRef ID) override;<br><br> private:<br>- DocumentStore &Store;<br>+ ClangdLSPServer &AST;<br> };<br><br> struct TextDocumentFormattingHandler : Handler {<br>- TextDocumentFormattingHandler<wbr>(JSONOutput &Output, DocumentStore &Store)<br>- : Handler(Output), Store(Store) {}<br>+ TextDocumentFormattingHandler<wbr>(JSONOutput &Output, ClangdLSPServer &AST)<br>+ : Handler(Output), AST(AST) {}<br><br> void handleMethod(llvm::yaml::Mappi<wbr>ngNode *Params, StringRef ID) override;<br><br> private:<br>- DocumentStore &Store;<br>+ ClangdLSPServer &AST;<br> };<br><br> struct CodeActionHandler : Handler {<br>- CodeActionHandler(JSONOutput &Output, ASTManager &AST)<br>+ CodeActionHandler(JSONOutput &Output, ClangdLSPServer &AST)<br> : Handler(Output), AST(AST) {}<br><br> void handleMethod(llvm::yaml::Mappi<wbr>ngNode *Params, StringRef ID) override;<br><br> private:<br>- ASTManager &AST;<br>+ ClangdLSPServer &AST;<br> };<br><br> struct CompletionHandler : Handler {<br>- CompletionHandler(JSONOutput &Output, ASTManager &AST)<br>+ CompletionHandler(JSONOutput &Output, ClangdLSPServer &AST)<br> : Handler(Output), AST(AST) {}<br><br> void handleMethod(llvm::yaml::Mappi<wbr>ngNode *Params, StringRef ID) override;<br><br> private:<br>- ASTManager &AST;<br>+ ClangdLSPServer &AST;<br> };<br><br> } // namespace clangd<br><br><br>______________________________<wbr>_________________<br>cfe-commits mailing list<br><a href="mailto:cfe-commits@lists.llvm.org" target="_blank">cfe-commits@lists.llvm.org</a><br><a href="http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits" target="_blank">http://lists.llvm.org/cgi-bin/<wbr>mailman/listinfo/cfe-commits</a><br></div></div></blockquote></div><br></div></div></div></blockquote></div><br></div></div></div></blockquote></div><br></div></div></blockquote></div><span class="HOEnZb"><font color="#888888"><br><br clear="all"><div><br></div>-- <br><div class="m_3771896676606961527gmail_signature" data-smartmail="gmail_signature"><div dir="ltr"><div><div dir="ltr"><div>Regards,</div><div>Ilya Biryukov</div></div></div></div></div>
</font></span></div>
</blockquote></div><br><br clear="all"><div><br></div>-- <br><div class="gmail_signature" data-smartmail="gmail_signature"><div dir="ltr"><div><div dir="ltr"><div>Regards,</div><div>Ilya Biryukov</div></div></div></div></div>
</div>