[PATCH] D29451: Add a prototype for clangd v0.1
Jusufadis Bakamovic via Phabricator via cfe-commits
cfe-commits at lists.llvm.org
Fri Feb 3 04:35:51 PST 2017
Adi added inline comments.
================
Comment at: clangd/JSONRPCDispatcher.h:25-32
+ virtual ~Handler() = default;
+
+ /// Called when the server receives a method call. This is supposed to return
+ /// a result on Outs.
+ virtual void handleMethod(llvm::yaml::MappingNode *Params, StringRef ID);
+ /// Called when the server receives a notification. No result should be
+ /// written to Outs.
----------------
klimek wrote:
> Adi wrote:
> > To avoid virtual dispatch one may rewrite this class in terms of CRTP. E.g.
> >
> > ```
> > template <typename T>
> > class Handler {
> > public:
> > Handler(llvm::raw_ostream &Outs, llvm::raw_ostream &Logs)
> > : Outs(Outs), Logs(Logs) {}
> > virtual ~Handler() = default;
> >
> > void handleMethod(const llvm::yaml::MappingMode *Params, StringRef ID) {
> > static_cast<T*>(this)->handleMethod(Params, ID);
> > }
> > void handleNotification(const llvm::yaml::MappingMode *Params) {
> > static_cast<T*>(this)->handleNotification(Params);
> > }
> >
> > protected:
> > llvm::raw_ostream &Outs;
> > llvm::raw_ostream &Logs;
> >
> > void writeMessage(const Twine &Message);
> > };
> > ```
> > And then use it as:
> >
> > ```
> > struct MyConcreteHandler : public Handler<MyConcreteHandler> {
> > MyConcreteHandler(llvm::raw_ostream &Outs, llvm::raw_ostream &Logs /* other params if necessary */)
> > : Handler(Outs, Logs) /* init other members */
> > {}
> >
> > void handleMethod(const llvm::yaml::MappingMode *Params, StringRef ID) {
> > // impl
> > }
> > void handleNotification(const llvm::yaml::MappingMode *Params) {
> > // impl
> > }
> > };
> > ```
> > To avoid virtual dispatch one may rewrite this class in terms of CRTP. E.g.
>
> Why would virtual dispatch be a problem here? It seems strictly simpler this way.
>
I've mentioned it only as an alternative approach. I was not implying that virtual dispatch is a problem. It's a matter of taste in this case really.
================
Comment at: clangd/Protocol.cpp:11
+// This file contains the serialization code for the LSP structs.
+// FIXME: This is extremely repetetive and ugly. Is there a better way?
+//
----------------
Adi wrote:
> To some degree it could be refactored. I would go for CRTP again here. It would simplify the code a little bit. E.g.
>
> ```
> template <typename T>
> struct Params {
> static llvm::Optional<T> parse(const llvm::yaml::MappingNode& node) {
> T Result;
> for (auto& NextKeyValue : node) {
> 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;
>
> Result = static_cast<T*>(this)->setParam(KeyValue, *Value); // or make Result an argument if worried about RVO
> if (!Result) // we can make this more neat by placing it inside a for loop condition (ranged-for doesn't allow this)
> return llvm::none;
> }
> return Result;
> }
> };
>
> struct TextDocumentIdentifier : public Params<TextDocumentIdentifier> {
> /// The text document's URI.
> std::string uri;
>
> llvm::Optional<TextDocumentIdentifier> setParam(StringRef KeyValue, const llvm::yaml::ScalarNode& Value) {
> TextDocumentIdentifier Result;
> 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;
> }
> };
>
> struct Position : public Params<Position> {
> /// Line position in a document (zero-based).
> int line;
>
> /// Character offset on a line in a document (zero-based).
> int character;
>
> llvm::Optional<Position> setParam(StringRef KeyValue, const llvm::yaml::ScalarNode& Value) {
> Position Result;
> 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;
> }
> };
> ```
>
> I am not really familiar with all of the dependencies here but I may suggest that one might also try to generalize this approach even further by encapsulating the handling of `KeyValue` into the common implementation. E.g.
>
> ```
> template <typename T>
> T fromKeyValueToParam(StringRef KeyValue, const llvm::yaml::ScalarNode& Value) {
> T Result;
> 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 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 if (KeyValue == "range")
> }
> else if (KeyValue == "range") {
> auto Parsed = Range::parse(Value);
> if (!Parsed)
> return llvm::None;
> Result.range = std::move(*Parsed);
> }
> else if ( ... ) {
> ...
> }
>
> ...
>
> return Result;
> }
> ```
>
> Then you would be able to handle everything in `Params<T>::parse()` without any other specialized code required to be written in concrete parameters. Code could look something like this:
>
> ```
> template <typename T>
> struct Params {
> static llvm::Optional<T> parse(const llvm::yaml::MappingNode& Params) {
> T 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;
>
> Result = fromKeyValueToParam(KeyValue, *Value);
> if (!Result) // we can make this more neat by placing it inside a for loop condition (ranged-for doesn't allow us that)
> return llvm::none;
> }
> return Result;
> }
> };
>
> struct Position : public Params<Position> {
> /// Line position in a document (zero-based).
> int line;
>
> /// Character offset on a line in a document (zero-based).
> int character;
> };
>
> struct TextDocumentIdentifier : public Params<TextDocumentIdentifier> {
> /// The text document's URI.
> std::string uri;
> };
> ```
`fromKeyValueToParam()` will of course not compile but you've got the idea. I think it can be achieved in this way somehow.
https://reviews.llvm.org/D29451
More information about the cfe-commits
mailing list