Add a prototype for clangd
clangd is a language server protocol implementation based on clang. It's
supposed to provide editor integration while not suffering from the
confined ABI of libclang.
This implementation is limited to the bare minimum functionality of
doing (whole-document) formatting and rangeFormatting. The JSON parsing
is based on LLVM's YAMLParser but yet most of the code of clangd is
currently dealing with JSON serialization and deserialization.
This was only tested with VS Code so far, mileage with other LSP clients
may vary.
Differential Revision: https://reviews.llvm.org/D29451
llvm-svn: 294291
diff --git a/clang-tools-extra/clangd/Protocol.cpp b/clang-tools-extra/clangd/Protocol.cpp
new file mode 100644
index 0000000..f87f6d0
--- /dev/null
+++ b/clang-tools-extra/clangd/Protocol.cpp
@@ -0,0 +1,412 @@
+//===--- Protocol.cpp - Language Server Protocol Implementation -----------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file contains the serialization code for the LSP structs.
+// FIXME: This is extremely repetetive and ugly. Is there a better way?
+//
+//===----------------------------------------------------------------------===//
+
+#include "Protocol.h"
+#include "clang/Basic/LLVM.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/Support/Format.h"
+#include "llvm/Support/raw_ostream.h"
+using namespace clang::clangd;
+
+llvm::Optional<TextDocumentIdentifier>
+TextDocumentIdentifier::parse(llvm::yaml::MappingNode *Params) {
+ TextDocumentIdentifier 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 =
+ dyn_cast_or_null<llvm::yaml::ScalarNode>(NextKeyValue.getValue());
+ if (!Value)
+ return llvm::None;
+
+ llvm::SmallString<10> Storage;
+ if (KeyValue == "uri") {
+ Result.uri = Value->getValue(Storage);
+ } else if (KeyValue == "version") {
+ // FIXME: parse version, but only for VersionedTextDocumentIdentifiers.
+ } else {
+ return llvm::None;
+ }
+ }
+ return Result;
+}
+
+llvm::Optional<Position> Position::parse(llvm::yaml::MappingNode *Params) {
+ Position 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 =
+ dyn_cast_or_null<llvm::yaml::ScalarNode>(NextKeyValue.getValue());
+ if (!Value)
+ return llvm::None;
+
+ llvm::SmallString<10> Storage;
+ if (KeyValue == "line") {
+ long long Val;
+ if (llvm::getAsSignedInteger(Value->getValue(Storage), 0, Val))
+ return llvm::None;
+ Result.line = Val;
+ } else if (KeyValue == "character") {
+ long long Val;
+ if (llvm::getAsSignedInteger(Value->getValue(Storage), 0, Val))
+ return llvm::None;
+ Result.character = Val;
+ } else {
+ return llvm::None;
+ }
+ }
+ return Result;
+}
+
+std::string Position::unparse(const Position &P) {
+ std::string Result;
+ llvm::raw_string_ostream(Result)
+ << llvm::format(R"({"line": %d, "character": %d})", P.line, P.character);
+ return Result;
+}
+
+llvm::Optional<Range> Range::parse(llvm::yaml::MappingNode *Params) {
+ Range 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 =
+ dyn_cast_or_null<llvm::yaml::MappingNode>(NextKeyValue.getValue());
+ if (!Value)
+ return llvm::None;
+
+ llvm::SmallString<10> Storage;
+ if (KeyValue == "start") {
+ auto Parsed = Position::parse(Value);
+ if (!Parsed)
+ return llvm::None;
+ Result.start = std::move(*Parsed);
+ } else if (KeyValue == "end") {
+ auto Parsed = Position::parse(Value);
+ if (!Parsed)
+ return llvm::None;
+ Result.end = std::move(*Parsed);
+ } else {
+ return llvm::None;
+ }
+ }
+ return Result;
+}
+
+std::string Range::unparse(const Range &P) {
+ std::string Result;
+ llvm::raw_string_ostream(Result) << llvm::format(
+ R"({"start": %s, "end": %s})", Position::unparse(P.start).c_str(),
+ Position::unparse(P.end).c_str());
+ return Result;
+}
+
+llvm::Optional<TextDocumentItem>
+TextDocumentItem::parse(llvm::yaml::MappingNode *Params) {
+ TextDocumentItem 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 =
+ dyn_cast_or_null<llvm::yaml::ScalarNode>(NextKeyValue.getValue());
+ if (!Value)
+ return llvm::None;
+
+ llvm::SmallString<10> Storage;
+ if (KeyValue == "uri") {
+ Result.uri = Value->getValue(Storage);
+ } else if (KeyValue == "languageId") {
+ Result.languageId = Value->getValue(Storage);
+ } else if (KeyValue == "version") {
+ long long Val;
+ if (llvm::getAsSignedInteger(Value->getValue(Storage), 0, Val))
+ return llvm::None;
+ Result.version = Val;
+ } else if (KeyValue == "text") {
+ Result.text = Value->getValue(Storage);
+ } else {
+ return llvm::None;
+ }
+ }
+ return Result;
+}
+
+llvm::Optional<TextEdit> TextEdit::parse(llvm::yaml::MappingNode *Params) {
+ TextEdit 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 == "range") {
+ auto *Map = dyn_cast<llvm::yaml::MappingNode>(Value);
+ if (!Map)
+ return llvm::None;
+ auto Parsed = Range::parse(Map);
+ if (!Parsed)
+ return llvm::None;
+ Result.range = std::move(*Parsed);
+ } else if (KeyValue == "newText") {
+ auto *Node = dyn_cast<llvm::yaml::ScalarNode>(Value);
+ if (!Node)
+ return llvm::None;
+ Result.newText = Node->getValue(Storage);
+ } else {
+ return llvm::None;
+ }
+ }
+ return Result;
+}
+
+std::string TextEdit::unparse(const TextEdit &P) {
+ std::string Result;
+ llvm::raw_string_ostream(Result) << llvm::format(
+ R"({"range": %s, "newText": "%s"})", Range::unparse(P.range).c_str(),
+ llvm::yaml::escape(P.newText).c_str());
+ return Result;
+}
+
+llvm::Optional<DidOpenTextDocumentParams>
+DidOpenTextDocumentParams::parse(llvm::yaml::MappingNode *Params) {
+ DidOpenTextDocumentParams 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 =
+ dyn_cast_or_null<llvm::yaml::MappingNode>(NextKeyValue.getValue());
+ if (!Value)
+ return llvm::None;
+
+ llvm::SmallString<10> Storage;
+ if (KeyValue == "textDocument") {
+ auto Parsed = TextDocumentItem::parse(Value);
+ if (!Parsed)
+ return llvm::None;
+ Result.textDocument = std::move(*Parsed);
+ } else {
+ return llvm::None;
+ }
+ }
+ return Result;
+}
+
+llvm::Optional<DidChangeTextDocumentParams>
+DidChangeTextDocumentParams::parse(llvm::yaml::MappingNode *Params) {
+ DidChangeTextDocumentParams 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 == "textDocument") {
+ auto *Map = dyn_cast<llvm::yaml::MappingNode>(Value);
+ if (!Map)
+ return llvm::None;
+ auto Parsed = TextDocumentIdentifier::parse(Map);
+ if (!Parsed)
+ return llvm::None;
+ Result.textDocument = std::move(*Parsed);
+ } else if (KeyValue == "contentChanges") {
+ 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 = TextDocumentContentChangeEvent::parse(I);
+ if (!Parsed)
+ return llvm::None;
+ Result.contentChanges.push_back(std::move(*Parsed));
+ }
+ } else {
+ return llvm::None;
+ }
+ }
+ return Result;
+}
+
+llvm::Optional<TextDocumentContentChangeEvent>
+TextDocumentContentChangeEvent::parse(llvm::yaml::MappingNode *Params) {
+ TextDocumentContentChangeEvent 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 =
+ dyn_cast_or_null<llvm::yaml::ScalarNode>(NextKeyValue.getValue());
+ if (!Value)
+ return llvm::None;
+
+ llvm::SmallString<10> Storage;
+ if (KeyValue == "text") {
+ Result.text = Value->getValue(Storage);
+ } else {
+ return llvm::None;
+ }
+ }
+ return Result;
+}
+
+llvm::Optional<FormattingOptions>
+FormattingOptions::parse(llvm::yaml::MappingNode *Params) {
+ FormattingOptions 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 =
+ dyn_cast_or_null<llvm::yaml::ScalarNode>(NextKeyValue.getValue());
+ if (!Value)
+ return llvm::None;
+
+ llvm::SmallString<10> Storage;
+ if (KeyValue == "tabSize") {
+ long long Val;
+ if (llvm::getAsSignedInteger(Value->getValue(Storage), 0, Val))
+ return llvm::None;
+ Result.tabSize = Val;
+ } else if (KeyValue == "insertSpaces") {
+ long long Val;
+ StringRef Str = Value->getValue(Storage);
+ if (llvm::getAsSignedInteger(Str, 0, Val)) {
+ if (Str == "true")
+ Val = 1;
+ else if (Str == "false")
+ Val = 0;
+ else
+ return llvm::None;
+ }
+ Result.insertSpaces = Val;
+ } else {
+ return llvm::None;
+ }
+ }
+ return Result;
+}
+
+std::string FormattingOptions::unparse(const FormattingOptions &P) {
+ std::string Result;
+ llvm::raw_string_ostream(Result) << llvm::format(
+ R"({"tabSize": %d, "insertSpaces": %d})", P.tabSize, P.insertSpaces);
+ return Result;
+}
+
+llvm::Optional<DocumentRangeFormattingParams>
+DocumentRangeFormattingParams::parse(llvm::yaml::MappingNode *Params) {
+ DocumentRangeFormattingParams 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 =
+ dyn_cast_or_null<llvm::yaml::MappingNode>(NextKeyValue.getValue());
+ if (!Value)
+ return llvm::None;
+
+ llvm::SmallString<10> Storage;
+ if (KeyValue == "textDocument") {
+ auto Parsed = TextDocumentIdentifier::parse(Value);
+ if (!Parsed)
+ return llvm::None;
+ Result.textDocument = std::move(*Parsed);
+ } else if (KeyValue == "range") {
+ auto Parsed = Range::parse(Value);
+ if (!Parsed)
+ return llvm::None;
+ Result.range = std::move(*Parsed);
+ } else if (KeyValue == "options") {
+ auto Parsed = FormattingOptions::parse(Value);
+ if (!Parsed)
+ return llvm::None;
+ Result.options = std::move(*Parsed);
+ } else {
+ return llvm::None;
+ }
+ }
+ return Result;
+}
+
+llvm::Optional<DocumentFormattingParams>
+DocumentFormattingParams::parse(llvm::yaml::MappingNode *Params) {
+ DocumentFormattingParams 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 =
+ dyn_cast_or_null<llvm::yaml::MappingNode>(NextKeyValue.getValue());
+ if (!Value)
+ return llvm::None;
+
+ llvm::SmallString<10> Storage;
+ if (KeyValue == "textDocument") {
+ auto Parsed = TextDocumentIdentifier::parse(Value);
+ if (!Parsed)
+ return llvm::None;
+ Result.textDocument = std::move(*Parsed);
+ } else if (KeyValue == "options") {
+ auto Parsed = FormattingOptions::parse(Value);
+ if (!Parsed)
+ return llvm::None;
+ Result.options = std::move(*Parsed);
+ } else {
+ return llvm::None;
+ }
+ }
+ return Result;
+}