summaryrefslogtreecommitdiffstats
path: root/clang-tools-extra/clangd/ClangdLSPServer.cpp
blob: 2a2e0bf971f0bfcf7edb552620ea127fa1e1f61b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
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"(]}})");
}
OpenPOWER on IntegriCloud