Skip to content

Commit

Permalink
[clangd] DidChangeConfiguration Notification
Browse files Browse the repository at this point in the history
Summary:

Implementation of DidChangeConfiguration notification handling in
clangd.  This currently only supports changing one setting: the path of
the compilation database to be used for the current project.   In other
words, it is no longer necessary to restart clangd with a different
command line argument in order to change the compilation database.

Reviewers: malaperle, krasimir, bkramer, ilya-biryukov

Subscribers: jkorous-apple, ioeric, simark, klimek, ilya-biryukov, arphaman, rwols, cfe-commits

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

Signed-off-by: Simon Marchi <simon.marchi@ericsson.com>
Signed-off-by: William Enright <william.enright@polymtl.ca>
llvm-svn: 325784
  • Loading branch information
Simon Marchi committed Feb 22, 2018
1 parent ed797a3 commit 5178f92
Show file tree
Hide file tree
Showing 13 changed files with 180 additions and 0 deletions.
12 changes: 12 additions & 0 deletions clang-tools-extra/clangd/ClangdLSPServer.cpp
Expand Up @@ -370,6 +370,18 @@ void ClangdLSPServer::onHover(TextDocumentPositionParams &Params) {
});
}

// FIXME: This function needs to be properly tested.
void ClangdLSPServer::onChangeConfiguration(
DidChangeConfigurationParams &Params) {
ClangdConfigurationParamsChange &Settings = Params.settings;

// Compilation database change.
if (Settings.compilationDatabasePath.hasValue()) {
CDB.setCompileCommandsDir(Settings.compilationDatabasePath.getValue());
Server.reparseOpenedFiles();
}
}

ClangdLSPServer::ClangdLSPServer(JSONOutput &Out, unsigned AsyncThreadsCount,
bool StorePreamblesInMemory,
const clangd::CodeCompleteOptions &CCOpts,
Expand Down
1 change: 1 addition & 0 deletions clang-tools-extra/clangd/ClangdLSPServer.h
Expand Up @@ -76,6 +76,7 @@ class ClangdLSPServer : private DiagnosticsConsumer, private ProtocolCallbacks {
void onCommand(ExecuteCommandParams &Params) override;
void onRename(RenameParams &Parames) override;
void onHover(TextDocumentPositionParams &Params) override;
void onChangeConfiguration(DidChangeConfigurationParams &Params) override;

std::vector<TextEdit> getFixIts(StringRef File, const clangd::Diagnostic &D);

Expand Down
5 changes: 5 additions & 0 deletions clang-tools-extra/clangd/ClangdServer.cpp
Expand Up @@ -554,6 +554,11 @@ void ClangdServer::scheduleReparseAndDiags(
WantDiags, std::move(Callback));
}

void ClangdServer::reparseOpenedFiles() {
for (const Path &FilePath : DraftMgr.getActiveFiles())
forceReparse(FilePath);
}

void ClangdServer::onFileEvent(const DidChangeWatchedFilesParams &Params) {
// FIXME: Do nothing for now. This will be used for indexing and potentially
// invalidating other caches.
Expand Down
5 changes: 5 additions & 0 deletions clang-tools-extra/clangd/ClangdServer.h
Expand Up @@ -163,6 +163,11 @@ class ClangdServer {
/// and AST and rebuild them from scratch.
void forceReparse(PathRef File);

/// Calls forceReparse() on all currently opened files.
/// As a result, this method may be very expensive.
/// This method is normally called when the compilation database is changed.
void reparseOpenedFiles();

/// Run code completion for \p File at \p Pos.
/// Request is processed asynchronously.
///
Expand Down
11 changes: 11 additions & 0 deletions clang-tools-extra/clangd/DraftStore.cpp
Expand Up @@ -21,6 +21,17 @@ VersionedDraft DraftStore::getDraft(PathRef File) const {
return It->second;
}

std::vector<Path> DraftStore::getActiveFiles() const {
std::lock_guard<std::mutex> Lock(Mutex);
std::vector<Path> ResultVector;

for (auto DraftIt = Drafts.begin(); DraftIt != Drafts.end(); DraftIt++)
if (DraftIt->second.Draft)
ResultVector.push_back(DraftIt->getKey());

return ResultVector;
}

DocVersion DraftStore::getVersion(PathRef File) const {
std::lock_guard<std::mutex> Lock(Mutex);

Expand Down
6 changes: 6 additions & 0 deletions clang-tools-extra/clangd/DraftStore.h
Expand Up @@ -40,6 +40,12 @@ class DraftStore {
/// \return version and contents of the stored document.
/// For untracked files, a (0, None) pair is returned.
VersionedDraft getDraft(PathRef File) const;

/// \return List of names of active drafts in this store. Drafts that were
/// removed (which still have an entry in the Drafts map) are not returned by
/// this function.
std::vector<Path> getActiveFiles() const;

/// \return version of the tracked document.
/// For untracked files, 0 is returned.
DocVersion getVersion(PathRef File) const;
Expand Down
6 changes: 6 additions & 0 deletions clang-tools-extra/clangd/GlobalCompilationDatabase.cpp
Expand Up @@ -51,6 +51,12 @@ DirectoryBasedGlobalCompilationDatabase::getFallbackCommand(
return C;
}

void DirectoryBasedGlobalCompilationDatabase::setCompileCommandsDir(Path P) {
std::lock_guard<std::mutex> Lock(Mutex);
CompileCommandsDir = P;
CompilationDatabases.clear();
}

void DirectoryBasedGlobalCompilationDatabase::setExtraFlagsForFile(
PathRef File, std::vector<std::string> ExtraFlags) {
std::lock_guard<std::mutex> Lock(Mutex);
Expand Down
3 changes: 3 additions & 0 deletions clang-tools-extra/clangd/GlobalCompilationDatabase.h
Expand Up @@ -61,6 +61,9 @@ class DirectoryBasedGlobalCompilationDatabase
/// Uses the default fallback command, adding any extra flags.
tooling::CompileCommand getFallbackCommand(PathRef File) const override;

/// Set the compile commands directory to \p P.
void setCompileCommandsDir(Path P);

/// Sets the extra flags that should be added to a file.
void setExtraFlagsForFile(PathRef File, std::vector<std::string> ExtraFlags);

Expand Down
10 changes: 10 additions & 0 deletions clang-tools-extra/clangd/Protocol.cpp
Expand Up @@ -497,5 +497,15 @@ json::Expr toJSON(const DocumentHighlight &DH) {
};
}

bool fromJSON(const json::Expr &Params, DidChangeConfigurationParams &CCP) {
json::ObjectMapper O(Params);
return O && O.map("settings", CCP.settings);
}

bool fromJSON(const json::Expr &Params, ClangdConfigurationParamsChange &CCPC) {
json::ObjectMapper O(Params);
return O && O.map("compilationDatabasePath", CCPC.compilationDatabasePath);
}

} // namespace clangd
} // namespace clang
14 changes: 14 additions & 0 deletions clang-tools-extra/clangd/Protocol.h
Expand Up @@ -324,6 +324,20 @@ struct DidChangeWatchedFilesParams {
};
bool fromJSON(const json::Expr &, DidChangeWatchedFilesParams &);

/// Clangd extension to manage a workspace/didChangeConfiguration notification
/// since the data received is described as 'any' type in LSP.
struct ClangdConfigurationParamsChange {
llvm::Optional<std::string> compilationDatabasePath;
};
bool fromJSON(const json::Expr &, ClangdConfigurationParamsChange &);

struct DidChangeConfigurationParams {
// We use this predefined struct because it is easier to use
// than the protocol specified type of 'any'.
ClangdConfigurationParamsChange settings;
};
bool fromJSON(const json::Expr &, DidChangeConfigurationParams &);

struct FormattingOptions {
/// Size of a tab in spaces.
int tabSize = 0;
Expand Down
2 changes: 2 additions & 0 deletions clang-tools-extra/clangd/ProtocolHandlers.cpp
Expand Up @@ -72,4 +72,6 @@ void clangd::registerCallbackHandlers(JSONRPCDispatcher &Dispatcher,
Register("workspace/executeCommand", &ProtocolCallbacks::onCommand);
Register("textDocument/documentHighlight",
&ProtocolCallbacks::onDocumentHighlight);
Register("workspace/didChangeConfiguration",
&ProtocolCallbacks::onChangeConfiguration);
}
1 change: 1 addition & 0 deletions clang-tools-extra/clangd/ProtocolHandlers.h
Expand Up @@ -52,6 +52,7 @@ class ProtocolCallbacks {
virtual void onRename(RenameParams &Parames) = 0;
virtual void onDocumentHighlight(TextDocumentPositionParams &Params) = 0;
virtual void onHover(TextDocumentPositionParams &Params) = 0;
virtual void onChangeConfiguration(DidChangeConfigurationParams &Params) = 0;
};

void registerCallbackHandlers(JSONRPCDispatcher &Dispatcher, JSONOutput &Out,
Expand Down
104 changes: 104 additions & 0 deletions clang-tools-extra/unittests/clangd/ClangdTests.cpp
Expand Up @@ -7,6 +7,7 @@
//
//===----------------------------------------------------------------------===//

#include "Annotations.h"
#include "ClangdLSPServer.h"
#include "ClangdServer.h"
#include "Matchers.h"
Expand Down Expand Up @@ -77,6 +78,43 @@ class ErrorCheckingDiagConsumer : public DiagnosticsConsumer {
VFSTag LastVFSTag = VFSTag();
};

/// For each file, record whether the last published diagnostics contained at
/// least one error.
class MultipleErrorCheckingDiagConsumer : public DiagnosticsConsumer {
public:
void
onDiagnosticsReady(PathRef File,
Tagged<std::vector<DiagWithFixIts>> Diagnostics) override {
bool HadError = diagsContainErrors(Diagnostics.Value);

std::lock_guard<std::mutex> Lock(Mutex);
LastDiagsHadError[File] = HadError;
}

/// Exposes all files consumed by onDiagnosticsReady in an unspecified order.
/// For each file, a bool value indicates whether the last diagnostics
/// contained an error.
std::vector<std::pair<Path, bool>> filesWithDiags() const {
std::vector<std::pair<Path, bool>> Result;
std::lock_guard<std::mutex> Lock(Mutex);

for (const auto &it : LastDiagsHadError) {
Result.emplace_back(it.first(), it.second);
}

return Result;
}

void clear() {
std::lock_guard<std::mutex> Lock(Mutex);
LastDiagsHadError.clear();
}

private:
mutable std::mutex Mutex;
llvm::StringMap<bool> LastDiagsHadError;
};

/// Replaces all patterns of the form 0x123abc with spaces
std::string replacePtrsInDump(std::string const &Dump) {
llvm::Regex RE("0x[0-9a-fA-F]+");
Expand Down Expand Up @@ -413,6 +451,72 @@ int main() { return 0; }
EXPECT_FALSE(DiagConsumer.hadErrorInLastDiags());
}

// Test ClangdServer.reparseOpenedFiles.
TEST_F(ClangdVFSTest, ReparseOpenedFiles) {
Annotations FooSource(R"cpp(
#ifdef MACRO
$one[[static void bob() {}]]
#else
$two[[static void bob() {}]]
#endif
int main () { bo^b (); return 0; }
)cpp");

Annotations BarSource(R"cpp(
#ifdef MACRO
this is an error
#endif
)cpp");

Annotations BazSource(R"cpp(
int hello;
)cpp");

MockFSProvider FS;
MockCompilationDatabase CDB;
MultipleErrorCheckingDiagConsumer DiagConsumer;
ClangdServer Server(CDB, DiagConsumer, FS,
/*AsyncThreadsCount=*/0,
/*StorePreamblesInMemory=*/true);

auto FooCpp = testPath("foo.cpp");
auto BarCpp = testPath("bar.cpp");
auto BazCpp = testPath("baz.cpp");

FS.Files[FooCpp] = "";
FS.Files[BarCpp] = "";
FS.Files[BazCpp] = "";

CDB.ExtraClangFlags = {"-DMACRO=1"};
Server.addDocument(FooCpp, FooSource.code());
Server.addDocument(BarCpp, BarSource.code());
Server.addDocument(BazCpp, BazSource.code());

EXPECT_THAT(DiagConsumer.filesWithDiags(),
UnorderedElementsAre(Pair(FooCpp, false), Pair(BarCpp, true),
Pair(BazCpp, false)));

auto Locations = runFindDefinitions(Server, FooCpp, FooSource.point());
EXPECT_TRUE(bool(Locations));
EXPECT_THAT(Locations->Value, ElementsAre(Location{URIForFile{FooCpp},
FooSource.range("one")}));

// Undefine MACRO, close baz.cpp.
CDB.ExtraClangFlags.clear();
DiagConsumer.clear();
Server.removeDocument(BazCpp);
Server.reparseOpenedFiles();

EXPECT_THAT(DiagConsumer.filesWithDiags(),
UnorderedElementsAre(Pair(FooCpp, false), Pair(BarCpp, false)));

Locations = runFindDefinitions(Server, FooCpp, FooSource.point());
EXPECT_TRUE(bool(Locations));
EXPECT_THAT(Locations->Value, ElementsAre(Location{URIForFile{FooCpp},
FooSource.range("two")}));
}

TEST_F(ClangdVFSTest, MemoryUsage) {
MockFSProvider FS;
ErrorCheckingDiagConsumer DiagConsumer;
Expand Down

0 comments on commit 5178f92

Please sign in to comment.