summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--clang-tools-extra/clangd/ClangdLSPServer.cpp4
-rw-r--r--clang-tools-extra/clangd/ClangdLSPServer.h1
-rw-r--r--clang-tools-extra/clangd/ClangdServer.cpp5
-rw-r--r--clang-tools-extra/clangd/ClangdServer.h2
-rw-r--r--clang-tools-extra/clangd/Protocol.cpp78
-rw-r--r--clang-tools-extra/clangd/Protocol.h27
-rw-r--r--clang-tools-extra/clangd/ProtocolHandlers.cpp24
-rw-r--r--clang-tools-extra/clangd/ProtocolHandlers.h1
-rw-r--r--clang-tools-extra/clangd/clients/clangd-vscode/package.json5
-rw-r--r--clang-tools-extra/clangd/clients/clangd-vscode/src/extension.ts8
-rw-r--r--clang-tools-extra/test/clangd/did-change-watch-files.test61
11 files changed, 214 insertions, 2 deletions
diff --git a/clang-tools-extra/clangd/ClangdLSPServer.cpp b/clang-tools-extra/clangd/ClangdLSPServer.cpp
index 19a89353e0f..0384423e151 100644
--- a/clang-tools-extra/clangd/ClangdLSPServer.cpp
+++ b/clang-tools-extra/clangd/ClangdLSPServer.cpp
@@ -73,6 +73,10 @@ void ClangdLSPServer::onDocumentDidChange(DidChangeTextDocumentParams Params,
Params.contentChanges[0].text);
}
+void ClangdLSPServer::onFileEvent(const DidChangeWatchedFilesParams &Params) {
+ Server.onFileEvent(Params);
+}
+
void ClangdLSPServer::onDocumentDidClose(DidCloseTextDocumentParams Params,
JSONOutput &Out) {
Server.removeDocument(Params.textDocument.uri.file);
diff --git a/clang-tools-extra/clangd/ClangdLSPServer.h b/clang-tools-extra/clangd/ClangdLSPServer.h
index 608ff9bf244..656252f868d 100644
--- a/clang-tools-extra/clangd/ClangdLSPServer.h
+++ b/clang-tools-extra/clangd/ClangdLSPServer.h
@@ -71,6 +71,7 @@ private:
JSONOutput &Out) override;
void onSwitchSourceHeader(TextDocumentIdentifier Params, StringRef ID,
JSONOutput &Out) override;
+ void onFileEvent(const DidChangeWatchedFilesParams &Params) override;
std::vector<clang::tooling::Replacement>
getFixIts(StringRef File, const clangd::Diagnostic &D);
diff --git a/clang-tools-extra/clangd/ClangdServer.cpp b/clang-tools-extra/clangd/ClangdServer.cpp
index 2cf2cb791eb..f5d03252422 100644
--- a/clang-tools-extra/clangd/ClangdServer.cpp
+++ b/clang-tools-extra/clangd/ClangdServer.cpp
@@ -424,3 +424,8 @@ ClangdServer::scheduleCancelRebuild(std::shared_ptr<CppFile> Resources) {
std::move(DeferredCancel));
return DoneFuture;
}
+
+void ClangdServer::onFileEvent(const DidChangeWatchedFilesParams &Params) {
+ // FIXME: Do nothing for now. This will be used for indexing and potentially
+ // invalidating other caches.
+}
diff --git a/clang-tools-extra/clangd/ClangdServer.h b/clang-tools-extra/clangd/ClangdServer.h
index b819264e46a..b42d2d6226d 100644
--- a/clang-tools-extra/clangd/ClangdServer.h
+++ b/clang-tools-extra/clangd/ClangdServer.h
@@ -268,6 +268,8 @@ public:
/// Waits until all requests to worker thread are finished and dumps AST for
/// \p File. \p File must be in the list of added documents.
std::string dumpAST(PathRef File);
+ /// Called when an event occurs for a watched file in the workspace.
+ void onFileEvent(const DidChangeWatchedFilesParams &Params);
private:
std::future<void>
diff --git a/clang-tools-extra/clangd/Protocol.cpp b/clang-tools-extra/clangd/Protocol.cpp
index e68add6e4e8..f45e07d17c1 100644
--- a/clang-tools-extra/clangd/Protocol.cpp
+++ b/clang-tools-extra/clangd/Protocol.cpp
@@ -447,6 +447,84 @@ DidChangeTextDocumentParams::parse(llvm::yaml::MappingNode *Params,
return Result;
}
+llvm::Optional<FileEvent> FileEvent::parse(llvm::yaml::MappingNode *Params,
+ clangd::Logger &Logger) {
+ llvm::Optional<FileEvent> Result = FileEvent();
+ for (auto &NextKeyValue : *Params) {
+ // We have to consume the whole MappingNode because it doesn't support
+ // skipping and we want to be able to parse further valid events.
+ if (!Result)
+ continue;
+
+ auto *KeyString = dyn_cast<llvm::yaml::ScalarNode>(NextKeyValue.getKey());
+ if (!KeyString) {
+ Result.reset();
+ continue;
+ }
+
+ llvm::SmallString<10> KeyStorage;
+ StringRef KeyValue = KeyString->getValue(KeyStorage);
+ auto *Value =
+ dyn_cast_or_null<llvm::yaml::ScalarNode>(NextKeyValue.getValue());
+ if (!Value) {
+ Result.reset();
+ continue;
+ }
+ llvm::SmallString<10> Storage;
+ if (KeyValue == "uri") {
+ Result->uri = URI::parse(Value);
+ } else if (KeyValue == "type") {
+ long long Val;
+ if (llvm::getAsSignedInteger(Value->getValue(Storage), 0, Val)) {
+ Result.reset();
+ continue;
+ }
+ Result->type = static_cast<FileChangeType>(Val);
+ if (Result->type < FileChangeType::Created ||
+ Result->type > FileChangeType::Deleted)
+ Result.reset();
+ } else {
+ logIgnoredField(KeyValue, Logger);
+ }
+ }
+ return Result;
+}
+
+llvm::Optional<DidChangeWatchedFilesParams>
+DidChangeWatchedFilesParams::parse(llvm::yaml::MappingNode *Params,
+ clangd::Logger &Logger) {
+ DidChangeWatchedFilesParams Result;
+ for (auto &NextKeyValue : *Params) {
+ auto *KeyString = dyn_cast<llvm::yaml::ScalarNode>(NextKeyValue.getKey());
+ if (!KeyString)
+ return llvm::None;
+
+ llvm::SmallString<10> KeyStorage;
+ StringRef KeyValue = KeyString->getValue(KeyStorage);
+ auto *Value = NextKeyValue.getValue();
+
+ llvm::SmallString<10> Storage;
+ if (KeyValue == "changes") {
+ auto *Seq = dyn_cast<llvm::yaml::SequenceNode>(Value);
+ if (!Seq)
+ return llvm::None;
+ for (auto &Item : *Seq) {
+ auto *I = dyn_cast<llvm::yaml::MappingNode>(&Item);
+ if (!I)
+ return llvm::None;
+ auto Parsed = FileEvent::parse(I, Logger);
+ if (Parsed)
+ Result.changes.push_back(std::move(*Parsed));
+ else
+ Logger.log("Failed to decode a FileEvent.\n");
+ }
+ } else {
+ logIgnoredField(KeyValue, Logger);
+ }
+ }
+ return Result;
+}
+
llvm::Optional<TextDocumentContentChangeEvent>
TextDocumentContentChangeEvent::parse(llvm::yaml::MappingNode *Params,
clangd::Logger &Logger) {
diff --git a/clang-tools-extra/clangd/Protocol.h b/clang-tools-extra/clangd/Protocol.h
index 71ab9573a58..a6fcca6a40a 100644
--- a/clang-tools-extra/clangd/Protocol.h
+++ b/clang-tools-extra/clangd/Protocol.h
@@ -237,6 +237,33 @@ struct DidChangeTextDocumentParams {
parse(llvm::yaml::MappingNode *Params, clangd::Logger &Logger);
};
+enum class FileChangeType {
+ /// The file got created.
+ Created = 1,
+ /// The file got changed.
+ Changed = 2,
+ /// The file got deleted.
+ Deleted = 3
+};
+
+struct FileEvent {
+ /// The file's URI.
+ URI uri;
+ /// The change type.
+ FileChangeType type;
+
+ static llvm::Optional<FileEvent> parse(llvm::yaml::MappingNode *Params,
+ clangd::Logger &Logger);
+};
+
+struct DidChangeWatchedFilesParams {
+ /// The actual file events.
+ std::vector<FileEvent> changes;
+
+ static llvm::Optional<DidChangeWatchedFilesParams>
+ parse(llvm::yaml::MappingNode *Params, clangd::Logger &Logger);
+};
+
struct FormattingOptions {
/// Size of a tab in spaces.
int tabSize;
diff --git a/clang-tools-extra/clangd/ProtocolHandlers.cpp b/clang-tools-extra/clangd/ProtocolHandlers.cpp
index 544c44c6f23..936c673ebab 100644
--- a/clang-tools-extra/clangd/ProtocolHandlers.cpp
+++ b/clang-tools-extra/clangd/ProtocolHandlers.cpp
@@ -226,6 +226,25 @@ private:
ProtocolCallbacks &Callbacks;
};
+struct WorkspaceDidChangeWatchedFilesHandler : Handler {
+ WorkspaceDidChangeWatchedFilesHandler(JSONOutput &Output,
+ ProtocolCallbacks &Callbacks)
+ : Handler(Output), Callbacks(Callbacks) {}
+
+ void handleNotification(llvm::yaml::MappingNode *Params) {
+ auto DCWFP = DidChangeWatchedFilesParams::parse(Params, Output);
+ if (!DCWFP) {
+ Output.log("Failed to decode DidChangeWatchedFilesParams.\n");
+ return;
+ }
+
+ Callbacks.onFileEvent(*DCWFP);
+ }
+
+private:
+ ProtocolCallbacks &Callbacks;
+};
+
} // namespace
void clangd::registerCallbackHandlers(JSONRPCDispatcher &Dispatcher,
@@ -264,5 +283,8 @@ void clangd::registerCallbackHandlers(JSONRPCDispatcher &Dispatcher,
llvm::make_unique<GotoDefinitionHandler>(Out, Callbacks));
Dispatcher.registerHandler(
"textDocument/switchSourceHeader",
- llvm::make_unique<SwitchSourceHeaderHandler>(Out, Callbacks));
+ llvm::make_unique<SwitchSourceHeaderHandler>(Out, Callbacks));
+ Dispatcher.registerHandler(
+ "workspace/didChangeWatchedFiles",
+ llvm::make_unique<WorkspaceDidChangeWatchedFilesHandler>(Out, Callbacks));
}
diff --git a/clang-tools-extra/clangd/ProtocolHandlers.h b/clang-tools-extra/clangd/ProtocolHandlers.h
index 70796d34bf3..8592e574c8d 100644
--- a/clang-tools-extra/clangd/ProtocolHandlers.h
+++ b/clang-tools-extra/clangd/ProtocolHandlers.h
@@ -51,6 +51,7 @@ public:
JSONOutput &Out) = 0;
virtual void onSwitchSourceHeader(TextDocumentIdentifier Params, StringRef ID,
JSONOutput &Out) = 0;
+ virtual void onFileEvent(const DidChangeWatchedFilesParams &Params) = 0;
};
void registerCallbackHandlers(JSONRPCDispatcher &Dispatcher, JSONOutput &Out,
diff --git a/clang-tools-extra/clangd/clients/clangd-vscode/package.json b/clang-tools-extra/clangd/clients/clangd-vscode/package.json
index 729297af172..c048d965d3b 100644
--- a/clang-tools-extra/clangd/clients/clangd-vscode/package.json
+++ b/clang-tools-extra/clangd/clients/clangd-vscode/package.json
@@ -51,6 +51,11 @@
"type": "string"
},
"description": "Arguments for clangd server"
+ },
+ "clangd.syncFileEvents": {
+ "type": "boolean",
+ "default": true,
+ "description": "Whether or not to send file events to clangd (File created, changed or deleted). This can be disabled for performance consideration."
}
}
}
diff --git a/clang-tools-extra/clangd/clients/clangd-vscode/src/extension.ts b/clang-tools-extra/clangd/clients/clangd-vscode/src/extension.ts
index c76e0a021ee..f89ddc97ffe 100644
--- a/clang-tools-extra/clangd/clients/clangd-vscode/src/extension.ts
+++ b/clang-tools-extra/clangd/clients/clangd-vscode/src/extension.ts
@@ -18,18 +18,24 @@ function getConfig<T>(option: string, defaultValue?: any) : T {
export function activate(context: vscode.ExtensionContext) {
const clangdPath = getConfig<string>('path');
const clangdArgs = getConfig<string[]>('arguments');
+ const syncFileEvents = getConfig<boolean>('syncFileEvents', true);
const serverOptions: vscodelc.ServerOptions = { command: clangdPath, args: clangdArgs };
+ const cppFileExtensions: string[] = ['cpp', 'c', 'cc', 'cxx', 'c++', 'm', 'mm', 'h', 'hh', 'hpp', 'hxx', 'inc'];
+ const cppFileExtensionsPattern = cppFileExtensions.join();
const clientOptions: vscodelc.LanguageClientOptions = {
// Register the server for C/C++ files
- documentSelector: ['c', 'cc', 'cpp', 'h', 'hh', 'hpp'],
+ documentSelector: cppFileExtensions,
uriConverters: {
// FIXME: by default the URI sent over the protocol will be percent encoded (see rfc3986#section-2.1)
// the "workaround" below disables temporarily the encoding until decoding
// is implemented properly in clangd
code2Protocol: (uri: vscode.Uri) : string => uri.toString(true),
protocol2Code: (uri: string) : vscode.Uri => vscode.Uri.parse(uri)
+ },
+ synchronize: !syncFileEvents ? undefined : {
+ fileEvents: vscode.workspace.createFileSystemWatcher('**/*.{' + cppFileExtensionsPattern + '}')
}
};
diff --git a/clang-tools-extra/test/clangd/did-change-watch-files.test b/clang-tools-extra/test/clangd/did-change-watch-files.test
new file mode 100644
index 00000000000..d9ef6b4c214
--- /dev/null
+++ b/clang-tools-extra/test/clangd/did-change-watch-files.test
@@ -0,0 +1,61 @@
+# RUN: clangd -run-synchronously < %s 2>&1 | FileCheck -check-prefix=STDERR %s
+# It is absolutely vital that this file has CRLF line endings.
+#
+# Test initialize request parameters with rootUri
+Content-Length: 143
+
+{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootUri":"file:///path/to/workspace","capabilities":{},"trace":"off"}}
+# CHECK: Content-Length: 466
+# CHECK: {"jsonrpc":"2.0","id":0,"result":{"capabilities":{
+# CHECK: "textDocumentSync": 1,
+# CHECK: "documentFormattingProvider": true,
+# CHECK: "documentRangeFormattingProvider": true,
+# CHECK: "documentOnTypeFormattingProvider": {"firstTriggerCharacter":"}","moreTriggerCharacter":[]},
+# CHECK: "codeActionProvider": true,
+# CHECK: "completionProvider": {"resolveProvider": false, "triggerCharacters": [".",">",":"]},
+# CHECK: "definitionProvider": true
+# CHECK: }}}
+#
+#Normal case
+Content-Length: 217
+
+{"jsonrpc":"2.0","method":"workspace/didChangeWatchedFiles","params":{"changes":[{"uri":"file:///path/to/file.cpp","type":1},{"uri":"file:///path/to/file2.cpp","type":2},{"uri":"file:///path/to/file3.cpp","type":3}]}}
+
+# Wrong event type, integer
+Content-Length: 173
+
+{"jsonrpc":"2.0","method":"workspace/didChangeWatchedFiles","params":{"changes":[{"uri":"file:///path/to/file2.cpp","type":0},{"uri":"file:///path/to/file3.cpp","type":4}]}}
+# STDERR: Failed to decode a FileEvent.
+# Wrong event type, string
+Content-Length: 132
+
+{"jsonrpc":"2.0","method":"workspace/didChangeWatchedFiles","params":{"changes":[{"uri":"file:///path/to/file2.cpp","type":"foo"}]}}
+# STDERR: Failed to decode a FileEvent.
+#Custom event field
+Content-Length: 143
+
+{"jsonrpc":"2.0","method":"workspace/didChangeWatchedFiles","params":{"changes":[{"uri":"file:///path/to/file2.cpp","type":1,"custom":"foo"}]}}
+# STDERR: Failed to decode a FileEvent.
+#Event field with object
+Content-Length: 140
+
+{"jsonrpc":"2.0","method":"workspace/didChangeWatchedFiles","params":{"changes":[{"uri":"file:///path/to/file2.cpp","type":{"foo":"bar"}}]}}
+# STDERR: Failed to decode a FileEvent.
+# Changes field with sequence but no object
+Content-Length: 86
+
+{"jsonrpc":"2.0","method":"workspace/didChangeWatchedFiles","params":{"changes":[""]}}
+# STDERR: Failed to decode DidChangeWatchedFilesParams.
+# Changes field with no sequence
+Content-Length: 84
+
+{"jsonrpc":"2.0","method":"workspace/didChangeWatchedFiles","params":{"changes":""}}
+# STDERR: Failed to decode DidChangeWatchedFilesParams.
+# Custom field
+Content-Length: 86
+
+{"jsonrpc":"2.0","method":"workspace/didChangeWatchedFiles","params":{"custom":"foo"}}
+# STDERR: Ignored unknown field "custom"
+Content-Length: 44
+
+{"jsonrpc":"2.0","id":3,"method":"shutdown"}
OpenPOWER on IntegriCloud