[clangd] Extract FsPath from file:// uri

Patch contributed by stanionascu!

rfc8089#appendix-E.2 specifies that paths can begin with a drive letter e.g. as file:///c:/.
In this case just consuming front file:// is not enough and the 3rd slash must be consumed to produce a valid path on windows.

The patch introduce a generic way of converting an uri to a filesystem path and back.

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

llvm-svn: 299758
diff --git a/clang-tools-extra/clangd/Protocol.cpp b/clang-tools-extra/clangd/Protocol.cpp
index 163d982..bcb796f 100644
--- a/clang-tools-extra/clangd/Protocol.cpp
+++ b/clang-tools-extra/clangd/Protocol.cpp
@@ -17,8 +17,44 @@
 #include "llvm/ADT/SmallString.h"
 #include "llvm/Support/Format.h"
 #include "llvm/Support/raw_ostream.h"
+#include "llvm/Support/Path.h"
 using namespace clang::clangd;
 
+
+URI URI::fromUri(llvm::StringRef uri) {
+  URI Result;
+  Result.uri = uri;
+  uri.consume_front("file://");
+  // For Windows paths e.g. /X:
+  if (uri.size() > 2 && uri[0] == '/' && uri[2] == ':')
+    uri.consume_front("/");
+  // Make sure that file paths are in native separators
+  Result.file = llvm::sys::path::convert_to_slash(uri);
+  return Result;
+}
+
+URI URI::fromFile(llvm::StringRef file) {
+  using namespace llvm::sys;
+  URI Result;
+  Result.file = file;
+  Result.uri = "file://";
+  // For Windows paths e.g. X:
+  if (file.size() > 1 && file[1] == ':')
+    Result.uri += "/";
+  // Make sure that uri paths are with posix separators
+  Result.uri += path::convert_to_slash(file, path::Style::posix);
+  return Result;
+}
+
+URI URI::parse(llvm::yaml::ScalarNode *Param) {
+  llvm::SmallString<10> Storage;
+  return URI::fromUri(Param->getValue(Storage));
+}
+
+std::string URI::unparse(const URI &U) {
+  return U.uri;
+}
+
 llvm::Optional<TextDocumentIdentifier>
 TextDocumentIdentifier::parse(llvm::yaml::MappingNode *Params) {
   TextDocumentIdentifier Result;
@@ -34,9 +70,8 @@
     if (!Value)
       return llvm::None;
 
-    llvm::SmallString<10> Storage;
     if (KeyValue == "uri") {
-      Result.uri = Value->getValue(Storage);
+      Result.uri = URI::parse(Value);
     } else if (KeyValue == "version") {
       // FIXME: parse version, but only for VersionedTextDocumentIdentifiers.
     } else {
@@ -142,7 +177,7 @@
 
     llvm::SmallString<10> Storage;
     if (KeyValue == "uri") {
-      Result.uri = Value->getValue(Storage);
+      Result.uri = URI::parse(Value);
     } else if (KeyValue == "languageId") {
       Result.languageId = Value->getValue(Storage);
     } else if (KeyValue == "version") {