Skip to content

Commit

Permalink
[ClangD] Refactor clangd into separate components
Browse files Browse the repository at this point in the history
Summary: Major refactoring to split LSP implementation, Clang API calls and threading(mostly synchronization)

Reviewers: bkramer, krasimir

Reviewed By: bkramer

Subscribers: cfe-commits, mgorny, klimek

Tags: #clang-tools-extra

Differential Revision: https://reviews.llvm.org/D33047

llvm-svn: 303067
  • Loading branch information
ilya-biryukov committed May 15, 2017
1 parent e890d5b commit 96a1363
Show file tree
Hide file tree
Showing 20 changed files with 1,199 additions and 737 deletions.
440 changes: 0 additions & 440 deletions clang-tools-extra/clangd/ASTManager.cpp

This file was deleted.

162 changes: 0 additions & 162 deletions clang-tools-extra/clangd/ASTManager.h

This file was deleted.

7 changes: 6 additions & 1 deletion clang-tools-extra/clangd/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
add_clang_executable(clangd
ASTManager.cpp
ClangdLSPServer.cpp
ClangdMain.cpp
ClangdServer.cpp
ClangdUnit.cpp
ClangdUnitStore.cpp
DraftStore.cpp
GlobalCompilationDatabase.cpp
JSONRPCDispatcher.cpp
Protocol.cpp
ProtocolHandlers.cpp
Expand Down
100 changes: 100 additions & 0 deletions clang-tools-extra/clangd/ClangdLSPServer.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
//===--- ClangdLSPServer.cpp - LSP server ------------------------*- C++-*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===---------------------------------------------------------------------===//

#include "ClangdLSPServer.h"
#include "JSONRPCDispatcher.h"

using namespace clang::clangd;
using namespace clang;

class ClangdLSPServer::LSPDiagnosticsConsumer : public DiagnosticsConsumer {
public:
LSPDiagnosticsConsumer(ClangdLSPServer &Server) : Server(Server) {}

virtual void onDiagnosticsReady(PathRef File,
std::vector<DiagWithFixIts> Diagnostics) {
Server.consumeDiagnostics(File, Diagnostics);
}

private:
ClangdLSPServer &Server;
};

ClangdLSPServer::ClangdLSPServer(JSONOutput &Out, bool RunSynchronously)
: Out(Out),
Server(llvm::make_unique<DirectoryBasedGlobalCompilationDatabase>(),
llvm::make_unique<LSPDiagnosticsConsumer>(*this),
RunSynchronously) {}

void ClangdLSPServer::openDocument(StringRef File, StringRef Contents) {
Server.addDocument(File, Contents);
}

void ClangdLSPServer::closeDocument(StringRef File) {
Server.removeDocument(File);
}

std::vector<CompletionItem> ClangdLSPServer::codeComplete(PathRef File,
Position Pos) {
return Server.codeComplete(File, Pos);
}

std::vector<clang::tooling::Replacement>
ClangdLSPServer::getFixIts(StringRef File, const clangd::Diagnostic &D) {
std::lock_guard<std::mutex> Lock(FixItsMutex);
auto DiagToFixItsIter = FixItsMap.find(File);
if (DiagToFixItsIter == FixItsMap.end())
return {};

const auto &DiagToFixItsMap = DiagToFixItsIter->second;
auto FixItsIter = DiagToFixItsMap.find(D);
if (FixItsIter == DiagToFixItsMap.end())
return {};

return FixItsIter->second;
}

std::string ClangdLSPServer::getDocument(PathRef File) {
return Server.getDocument(File);
}

void ClangdLSPServer::consumeDiagnostics(
PathRef File, std::vector<DiagWithFixIts> Diagnostics) {
std::string DiagnosticsJSON;

DiagnosticToReplacementMap LocalFixIts; // Temporary storage
for (auto &DiagWithFixes : Diagnostics) {
auto Diag = DiagWithFixes.Diag;
DiagnosticsJSON +=
R"({"range":)" + Range::unparse(Diag.range) +
R"(,"severity":)" + std::to_string(Diag.severity) +
R"(,"message":")" + llvm::yaml::escape(Diag.message) +
R"("},)";

// We convert to Replacements to become independent of the SourceManager.
auto &FixItsForDiagnostic = LocalFixIts[Diag];
std::copy(DiagWithFixes.FixIts.begin(), DiagWithFixes.FixIts.end(),
std::back_inserter(FixItsForDiagnostic));
}

// Cache FixIts
{
// FIXME(ibiryukov): should be deleted when documents are removed
std::lock_guard<std::mutex> Lock(FixItsMutex);
FixItsMap[File] = LocalFixIts;
}

// Publish diagnostics.
if (!DiagnosticsJSON.empty())
DiagnosticsJSON.pop_back(); // Drop trailing comma.
Out.writeMessage(
R"({"jsonrpc":"2.0","method":"textDocument/publishDiagnostics","params":{"uri":")" +
URI::fromFile(File).uri + R"(","diagnostics":[)" + DiagnosticsJSON +
R"(]}})");
}
79 changes: 79 additions & 0 deletions clang-tools-extra/clangd/ClangdLSPServer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
//===--- ClangdLSPServer.h - LSP server --------------------------*- C++-*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===---------------------------------------------------------------------===//

#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_CLANGDLSPSERVER_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_CLANGDLSPSERVER_H

#include "ClangdServer.h"
#include "Path.h"
#include "Protocol.h"
#include "clang/Tooling/Core/Replacement.h"

namespace clang {
namespace clangd {

class JSONOutput;

/// This class serves as an intermediate layer of LSP server implementation,
/// glueing the JSON LSP protocol layer and ClangdServer together. It doesn't
/// directly handle input from LSP client.
/// Most methods are synchronous and return their result directly, but
/// diagnostics are provided asynchronously when ready via
/// JSONOutput::writeMessage.
class ClangdLSPServer {
public:
ClangdLSPServer(JSONOutput &Out, bool RunSynchronously);

/// Update the document text for \p File with \p Contents, schedule update of
/// diagnostics. Out.writeMessage will called to push diagnostics to LSP
/// client asynchronously when they are ready.
void openDocument(PathRef File, StringRef Contents);
/// Stop tracking the document for \p File.
void closeDocument(PathRef File);

/// Run code completion synchronously.
std::vector<CompletionItem> codeComplete(PathRef File, Position Pos);

/// Get the fixes associated with a certain diagnostic in a specified file as
/// replacements.
///
/// This function is thread-safe. It returns a copy to avoid handing out
/// references to unguarded data.
std::vector<clang::tooling::Replacement>
getFixIts(StringRef File, const clangd::Diagnostic &D);

/// Get the current document contents stored for \p File.
/// FIXME(ibiryukov): This function is here to allow implementation of
/// formatCode from ProtocolHandlers.cpp. We should move formatCode to
/// ClangdServer class and remove this function from public interface.
std::string getDocument(PathRef File);

private:
class LSPDiagnosticsConsumer;

/// Function that will be called on a separate thread when diagnostics are
/// ready. Sends the Dianostics to LSP client via Out.writeMessage and caches
/// corresponding fixits in the FixItsMap.
void consumeDiagnostics(PathRef File,
std::vector<DiagWithFixIts> Diagnostics);

JSONOutput &Out;
ClangdServer Server;

std::mutex FixItsMutex;
typedef std::map<clangd::Diagnostic, std::vector<clang::tooling::Replacement>>
DiagnosticToReplacementMap;
/// Caches FixIts per file and diagnostics
llvm::StringMap<DiagnosticToReplacementMap> FixItsMap;
};

} // namespace clangd
} // namespace clang

#endif
Loading

0 comments on commit 96a1363

Please sign in to comment.