[clang-tools-extra] r317486 - Adds a json::Expr type to represent intermediate JSON expressions.

Ilya Biryukov via cfe-commits cfe-commits at lists.llvm.org
Wed Nov 8 08:07:52 PST 2017


Clang remembers the last name used to access the file in FileManager and we
get the filename from FileManager later, so it's not that surprising that
it all broke apart.
Sam said that using file:///c:/main.cpp crashes clang on Linux for some
reason, we'll probably have to resort to regexes as an immediate solution
and figure out how to fix the crash later.

On Wed, Nov 8, 2017 at 8:47 PM, Marc-André Laperle <
marc-andre.laperle at ericsson.com> wrote:

> +1 for file://c:/main.cpp
>
> (A bit surprised we only see this problem now though?)
>
> Marc-André
>
> ------------------------------
> *From:* Ilya Biryukov <ibiryukov at google.com>
> *Sent:* Wednesday, November 8, 2017 8:29 AM
> *To:* Sam McCall
> *Cc:* Marc-André Laperle; cfe-commits; Yung, Douglas
> *Subject:* Re: [clang-tools-extra] r317486 - Adds a json::Expr type to
> represent intermediate JSON expressions.
>
> Yeah and `file://c:/main.cpp` looks like the best option to me.
>
> On Wed, Nov 8, 2017 at 6:26 PM, Sam McCall <sam.mccall at gmail.com> wrote:
>
> Oops, +list.
>
> r317686 makes just this test fuzzy, to unbreak the buildbot.
>
> On Wed, Nov 8, 2017 at 1:15 PM, Sam McCall <sam.mccall at gmail.com> wrote:
>
> On Wed, Nov 8, 2017 at 12:55 PM, Ilya Biryukov <ibiryukov at google.com>
> wrote:
>
> What's the exact problem with "file://" URIs on Windows? How to properly
> convert the Windows-style absolute paths into those? (i.e.
> "C:\Users\Admin\MyProject\Main.cpp"?)
> If so, there is an overview in Wikipedia (https://en.wikipedia.org/wiki
> /File_URI_scheme#Windows_2). In short, they should look this way:
> file:///c/Users/Admin/MyProject/Main.cpp..
>
> nit: the c needs to keep its colon: file:///c:/Users/...
>
> Yeah, when I thought about this some more, it's mostly that the tests
> assume we can pretend there's a file /main.cpp = file:///main.cpp.
> On windows, canonicalizing that filename results in C:\main.cpp =
> file:///C:/main.cpp, which is not what our tests expect.
>
> Options I see:
>  - make every test that checks a filename fuzzy (this seems doomed to
> failure)
>  - find a URI that works with both unix and windows paths
> (file:///C:/main.cpp?)
>  - avoid canonicalizing filenames in ways visible to tests
>  - ???
>
>
> On Wed, Nov 8, 2017 at 4:37 PM, Sam McCall <sam.mccall at gmail.com> wrote:
>
> I think all the JSON-related windows breakages are now fixed, and the
> remaining definitions.test breakage was caused by r317585.
> (Currently trying to get a windows machine to verify this)
>
> I have to confess I don't know much about how file:// URIs should work on
> windows. Marc-Andre - any thoughts, or maybe we should revert until we can
> work this one out?
>
> On Wed, Nov 8, 2017 at 3:39 AM, Yung, Douglas <douglas.yung at sony.com>
> wrote:
>
> Hi Sam,
>
> The test definitions.test is currently failing on the PS4 Windows bot
> because the URI returned is of the format "file:///C:/main.cpp" instead of
> the expected "file:///main.cpp" when run on a Windows machine. Can you
> please fix this ASAP so that we can get the bot green again?
>
> Here you can see the failure on the build bot:
> http://lab.llvm.org:8011/builders/llvm-clang-lld-x86_64-scei
> -ps4-windows10pro-fast/builds/13343
>
> You can reproduce the failure by feeding the following file to clangd on
> Windows:
>
> # Begin failure.test
> Content-Length: 125
>
> {"jsonrpc":"2.0","id":0,"method":"initialize","params":{"pro
> cessId":123,"rootPath":"clangd","capabilities":{},"trace":"off"}}
>
> Content-Length: 217
>
> {"jsonrpc":"2.0","method":"textDocument/didChange","params":
> {"textDocument":{"uri":"file:///main.cpp","version":7},"contentChanges":[{"text":"#define
> FOO 1\nint a = FOO;\n#define FOO 2\nint b = FOO;\n#undef FOO\n"}]}}
>
> Content-Length: 148
>
> {"jsonrpc":"2.0","id":1,"method":"textDocument/definition","
> params":{"textDocument":{"uri":"file:///main.cpp"},"position
> ":{"line":1,"character":8}}}
> # End failure.test
>
>
> This is cut down from test\clangd\definitions.test. The failure is on line
> 337 where it is looking for this:
>
> # CHECK-NEXT:      "uri": "file:///main.cpp"
>
> However, clangd is generating:
>
> "uri": "file:///C:/main.cpp"
>
> If you could fix this as soon as possible it would be greatly appreciated
> since it is causing the PS4 Windows bot to be red.
>
> Thanks!
>
> Douglas Yung
>
> > -----Original Message-----
> > From: cfe-commits [mailto:cfe-commits-bounces at lists.llvm.org] On Behalf
> Of Sam
> > McCall via cfe-commits
> > Sent: Monday, November 06, 2017 7:41
> > To: cfe-commits at lists.llvm.org
> > Subject: [clang-tools-extra] r317486 - Adds a json::Expr type to
> represent
> > intermediate JSON expressions.
> >
> > Author: sammccall
> > Date: Mon Nov  6 07:40:30 2017
> > New Revision: 317486
> >
> > URL: http://llvm.org/viewvc/llvm-project?rev=317486&view=rev
> > Log:
> > Adds a json::Expr type to represent intermediate JSON expressions.
> >
> > Summary:
> > This form can be created with a nice clang-format-friendly literal
> syntax,
> > and gets escaping right. It knows how to call unparse() on our Protocol
> types.
> > All the places where we pass around JSON internally now use this type.
> >
> > Object properties are sorted (stored as std::map) and so serialization is
> > canonicalized, with optional prettyprinting (triggered by a -pretty
> flag).
> > This makes the lit tests much nicer to read and somewhat nicer to debug.
> > (Unfortunately the completion tests use CHECK-DAG, which only has
> > line-granularity, so pretty-printing is disabled there. In future we
> > could make completion ordering deterministic, or switch to unittests).
> >
> > Compared to the current approach, it has some efficiencies like avoiding
> > copies
> > of string literals used as object keys, but is probably slower overall.
> > I think the code/test quality benefits are worth it.
> >
> > This patch doesn't attempt to do anything about JSON *parsing*.
> > It takes direction from the proposal in this doc[1], but is limited in
> scope
> > and visibility, for now.
> > I am of half a mind just to use Expr as the target of a parser, and
> maybe do a
> > little string deduplication, but not bother with clever memory
> allocation.
> > That would be simple, and fast enough for clangd...
> > [1]
> > https://docs.google.com/document/d/1OEF9IauWwNuSigZzvvbjc1cV
> S1uGHRyGTXaoy3DjqM
> > 4/edit
> >
> > +cc d0k so he can tell me not to use std::map.
> >
> > Reviewers: ioeric, malaperle
> >
> > Subscribers: bkramer, ilya-biryukov, mgorny, klimek
> >
> > Differential Revision: https://reviews.llvm.org/D39435
> >
> > Added:
> >     clang-tools-extra/trunk/clangd/JSONExpr.cpp
> >     clang-tools-extra/trunk/clangd/JSONExpr.h
> >     clang-tools-extra/trunk/unittests/clangd/JSONExprTests.cpp
> > Modified:
> >     clang-tools-extra/trunk/clangd/CMakeLists.txt
> >     clang-tools-extra/trunk/clangd/ClangdLSPServer.cpp
> >     clang-tools-extra/trunk/clangd/JSONRPCDispatcher.cpp
> >     clang-tools-extra/trunk/clangd/JSONRPCDispatcher.h
> >     clang-tools-extra/trunk/clangd/Protocol.cpp
> >     clang-tools-extra/trunk/clangd/Protocol.h
> >     clang-tools-extra/trunk/clangd/tool/ClangdMain.cpp
> >     clang-tools-extra/trunk/test/clangd/authority-less-uri.test
> >     clang-tools-extra/trunk/test/clangd/completion-items-kinds.test
> >     clang-tools-extra/trunk/test/clangd/completion-priorities.test
> >     clang-tools-extra/trunk/test/clangd/completion-qualifiers.test
> >     clang-tools-extra/trunk/test/clangd/completion-snippet.test
> >     clang-tools-extra/trunk/test/clangd/completion.test
> >     clang-tools-extra/trunk/test/clangd/definitions.test
> >     clang-tools-extra/trunk/test/clangd/diagnostics-preamble.test
> >     clang-tools-extra/trunk/test/clangd/diagnostics.test
> >     clang-tools-extra/trunk/test/clangd/did-change-watch-files.test
> >     clang-tools-extra/trunk/test/clangd/execute-command.test
> >     clang-tools-extra/trunk/test/clangd/extra-flags.test
> >     clang-tools-extra/trunk/test/clangd/fixits.test
> >     clang-tools-extra/trunk/test/clangd/formatting.test
> >     clang-tools-extra/trunk/test/clangd/initialize-params-invalid.test
> >     clang-tools-extra/trunk/test/clangd/initialize-params.test
> >     clang-tools-extra/trunk/test/clangd/input-mirror.test
> >     clang-tools-extra/trunk/test/clangd/protocol.test
> >     clang-tools-extra/trunk/test/clangd/signature-help.test
> >     clang-tools-extra/trunk/test/clangd/unsupported-method.test
> >     clang-tools-extra/trunk/unittests/clangd/CMakeLists.txt
> >
> > Modified: clang-tools-extra/trunk/clangd/CMakeLists.txt
> > URL: http://llvm.org/viewvc/llvm-project/clang-tools-
> > extra/trunk/clangd/CMakeLists.txt?rev=317486&r1=317485&r2=31
> 7486&view=diff
> > ============================================================
> ==================
> > --- clang-tools-extra/trunk/clangd/CMakeLists.txt (original)
> > +++ clang-tools-extra/trunk/clangd/CMakeLists.txt Mon Nov  6 07:40:30
> 2017
> > @@ -10,6 +10,7 @@ add_clang_library(clangDaemon
> >    DraftStore.cpp
> >    GlobalCompilationDatabase.cpp
> >    JSONRPCDispatcher.cpp
> > +  JSONExpr.cpp
> >    Logger.cpp
> >    Protocol.cpp
> >    ProtocolHandlers.cpp
> >
> > Modified: clang-tools-extra/trunk/clangd/ClangdLSPServer.cpp
> > URL: http://llvm.org/viewvc/llvm-project/clang-tools-
> > extra/trunk/clangd/ClangdLSPServer.cpp?rev=317486&r1=317485&
> r2=317486&view=dif
> > f
> > ============================================================
> ==================
> > --- clang-tools-extra/trunk/clangd/ClangdLSPServer.cpp (original)
> > +++ clang-tools-extra/trunk/clangd/ClangdLSPServer.cpp Mon Nov  6
> 07:40:30
> > 2017
> > @@ -20,35 +20,46 @@ namespace {
> >  std::vector<TextEdit>
> >  replacementsToEdits(StringRef Code,
> >                      const std::vector<tooling::Replacement>
> &Replacements) {
> > -  std::vector<TextEdit> Edits;
> >    // Turn the replacements into the format specified by the Language
> Server
> > -  // Protocol.
> > +  // Protocol. Fuse them into one big JSON array.
> > +  std::vector<TextEdit> Edits;
> >    for (auto &R : Replacements) {
> >      Range ReplacementRange = {
> >          offsetToPosition(Code, R.getOffset()),
> >          offsetToPosition(Code, R.getOffset() + R.getLength())};
> >      Edits.push_back({ReplacementRange, R.getReplacementText()});
> >    }
> > -
> >    return Edits;
> >  }
> >
> >  } // namespace
> >
> >  void ClangdLSPServer::onInitialize(Ctx C, InitializeParams &Params) {
> > -  C.reply(
> > -      R"({"capabilities":{
> > -          "textDocumentSync": 1,
> > -          "documentFormattingProvider": true,
> > -          "documentRangeFormattingProvider": true,
> > -          "documentOnTypeFormattingProvider":
> > {"firstTriggerCharacter":"}","moreTriggerCharacter":[]},
> > -          "codeActionProvider": true,
> > -          "completionProvider": {"resolveProvider": false,
> > "triggerCharacters": [".",">",":"]},
> > -          "signatureHelpProvider": {"triggerCharacters": ["(",","]},
> > -          "definitionProvider": true,
> > -          "executeCommandProvider": {"commands": [")" +
> > -      ExecuteCommandParams::CLANGD_APPLY_FIX_COMMAND + R"("]}
> > -        }})");
> > +  C.reply(json::obj{
> > +      {"textDocumentSync", 1},
> > +      {"documentFormattingProvider", true},
> > +      {"documentRangeFormattingProvider", true},
> > +      {"documentOnTypeFormattingProvider",
> > +       json::obj{
> > +           {"firstTriggerCharacter", "}"},
> > +           {"moreTriggerCharacter", {}},
> > +       }},
> > +      {"codeActionProvider", true},
> > +      {"completionProvider",
> > +       json::obj{
> > +           {"resolveProvider", false},
> > +           {"triggerCharacters", {".", ">", ":"}},
> > +       }},
> > +      {"signatureHelpProvider",
> > +       json::obj{
> > +           {"triggerCharacters", {"(", ","}},
> > +       }},
> > +      {"definitionProvider", true},
> > +      {"executeCommandProvider",
> > +       json::obj{
> > +           {"commands", {ExecuteCommandParams::CLANGD_
> APPLY_FIX_COMMAND}},
> > +       }},
> > +  });
> >    if (Params.rootUri && !Params.rootUri->file.empty())
> >      Server.setRootPath(Params.rootUri->file);
> >    else if (Params.rootPath && !Params.rootPath->empty())
> > @@ -58,7 +69,7 @@ void ClangdLSPServer::onInitialize(Ctx C
> >  void ClangdLSPServer::onShutdown(Ctx C, ShutdownParams &Params) {
> >    // Do essentially nothing, just say we're ready to exit.
> >    ShutdownRequestReceived = true;
> > -  C.reply("null");
> > +  C.reply(nullptr);
> >  }
> >
> >  void ClangdLSPServer::onExit(Ctx C, ExitParams &Params) { IsDone =
> true; }
> > @@ -98,7 +109,7 @@ void ClangdLSPServer::onCommand(Ctx C, E
> >
> >      ApplyWorkspaceEditParams ApplyEdit;
> >      ApplyEdit.edit = *Params.workspaceEdit;
> > -    C.reply("\"Fix applied.\"");
> > +    C.reply("Fix applied.");
> >      // We don't need the response so id == 1 is OK.
> >      // Ideally, we would wait for the response and if there is no
> error, we
> >      // would reply success/failure to the original RPC.
> > @@ -121,51 +132,45 @@ void ClangdLSPServer::onDocumentOnTypeFo
> >      Ctx C, DocumentOnTypeFormattingParams &Params) {
> >    auto File = Params.textDocument.uri.file;
> >    std::string Code = Server.getDocument(File);
> > -  std::string Edits = TextEdit::unparse(
> > -      replacementsToEdits(Code, Server.formatOnType(File,
> Params.position)));
> > -  C.reply(Edits);
> > +  C.reply(json::ary(
> > +      replacementsToEdits(Code, Server.formatOnType(File,
> > Params.position))));
> >  }
> >
> >  void ClangdLSPServer::onDocumentRangeFormatting(
> >      Ctx C, DocumentRangeFormattingParams &Params) {
> >    auto File = Params.textDocument.uri.file;
> >    std::string Code = Server.getDocument(File);
> > -  std::string Edits = TextEdit::unparse(
> > -      replacementsToEdits(Code, Server.formatRange(File,
> Params.range)));
> > -  C.reply(Edits);
> > +  C.reply(json::ary(
> > +      replacementsToEdits(Code, Server.formatRange(File,
> Params.range))));
> >  }
> >
> >  void ClangdLSPServer::onDocumentFormatting(Ctx C,
> >                                             DocumentFormattingParams
> &Params)
> > {
> >    auto File = Params.textDocument.uri.file;
> >    std::string Code = Server.getDocument(File);
> > -  std::string Edits =
> > -      TextEdit::unparse(replacementsToEdits(Code,
> Server.formatFile(File)));
> > -  C.reply(Edits);
> > +  C.reply(json::ary(replacementsToEdits(Code,
> Server.formatFile(File))));
> >  }
> >
> >  void ClangdLSPServer::onCodeAction(Ctx C, CodeActionParams &Params) {
> >    // We provide a code action for each diagnostic at the requested
> location
> >    // which has FixIts available.
> >    std::string Code = Server.getDocument(Params.textDocument.uri.file);
> > -  std::string Commands;
> > +  json::ary Commands;
> >    for (Diagnostic &D : Params.context.diagnostics) {
> >      std::vector<clang::tooling::Replacement> Fixes =
> >          getFixIts(Params.textDocument.uri.file, D);
> >      auto Edits = replacementsToEdits(Code, Fixes);
> > -    WorkspaceEdit WE;
> > -    WE.changes = {{llvm::yaml::escape(Params.textDocument.uri.uri),
> Edits}};
> > -
> > -    if (!Edits.empty())
> > -      Commands +=
> > -          R"({"title":"Apply FixIt ')" + llvm::yaml::escape(D.message) +
> > -          R"('", "command": ")" +
> > -          ExecuteCommandParams::CLANGD_APPLY_FIX_COMMAND +
> > -          R"(", "arguments": [)" + WorkspaceEdit::unparse(WE) +
> R"(]},)";
> > +    if (!Edits.empty()) {
> > +      WorkspaceEdit WE;
> > +      WE.changes = {{Params.textDocument.uri.uri, std::move(Edits)}};
> > +      Commands.push_back(json::obj{
> > +          {"title", llvm::formatv("Apply FixIt {0}", D.message)},
> > +          {"command", ExecuteCommandParams::CLANGD_APPLY_FIX_COMMAND},
> > +          {"arguments", {WE}},
> > +      });
> > +    }
> >    }
> > -  if (!Commands.empty())
> > -    Commands.pop_back();
> > -  C.reply("[" + Commands + "]");
> > +  C.reply(std::move(Commands));
> >  }
> >
> >  void ClangdLSPServer::onCompletion(Ctx C, TextDocumentPositionParams
> &Params)
> > {
> > @@ -177,15 +182,7 @@ void ClangdLSPServer::onCompletion(Ctx C
> >                            // had an API that would allow to attach
> callbacks
> > to
> >                            // futures returned by ClangdServer.
> >                     .Value;
> > -
> > -  std::string Completions;
> > -  for (const auto &Item : Items) {
> > -    Completions += CompletionItem::unparse(Item);
> > -    Completions += ",";
> > -  }
> > -  if (!Completions.empty())
> > -    Completions.pop_back();
> > -  C.reply("[" + Completions + "]");
> > +  C.reply(json::ary(Items));
> >  }
> >
> >  void ClangdLSPServer::onSignatureHelp(Ctx C,
> > @@ -195,7 +192,7 @@ void ClangdLSPServer::onSignatureHelp(Ct
> >        Position{Params.position.line, Params.position.character});
> >    if (!SignatureHelp)
> >      return C.replyError(-32602, llvm::toString(SignatureHelp.t
> akeError()));
> > -  C.reply(SignatureHelp::unparse(SignatureHelp->Value));
> > +  C.reply(SignatureHelp->Value);
> >  }
> >
> >  void ClangdLSPServer::onGoToDefinition(Ctx C,
> > @@ -205,22 +202,14 @@ void ClangdLSPServer::onGoToDefinition(C
> >        Position{Params.position.line, Params.position.character});
> >    if (!Items)
> >      return C.replyError(-32602, llvm::toString(Items.takeError()));
> > -
> > -  std::string Locations;
> > -  for (const auto &Item : Items->Value) {
> > -    Locations += Location::unparse(Item);
> > -    Locations += ",";
> > -  }
> > -  if (!Locations.empty())
> > -    Locations.pop_back();
> > -  C.reply("[" + Locations + "]");
> > +  C.reply(json::ary(Items->Value));
> >  }
> >
> >  void ClangdLSPServer::onSwitchSourceHeader(Ctx C,
> >                                             TextDocumentIdentifier
> &Params) {
> >    llvm::Optional<Path> Result = Server.switchSourceHeader(Para
> ms.uri.file);
> >    std::string ResultUri;
> > -  C.reply(Result ? URI::unparse(URI::fromFile(*Result)) : R"("")");
> > +  C.reply(Result ? URI::fromFile(*Result).uri : "");
> >  }
> >
> >  ClangdLSPServer::ClangdLSPServer(JSONOutput &Out, unsigned
> AsyncThreadsCount,
> > @@ -270,17 +259,16 @@ ClangdLSPServer::getFixIts(StringRef Fil
> >
> >  void ClangdLSPServer::onDiagnosticsReady(
> >      PathRef File, Tagged<std::vector<DiagWithFixIts>> Diagnostics) {
> > -  std::string DiagnosticsJSON;
> > +  json::ary DiagnosticsJSON;
> >
> >    DiagnosticToReplacementMap LocalFixIts; // Temporary storage
> >    for (auto &DiagWithFixes : Diagnostics.Value) {
> >      auto Diag = DiagWithFixes.Diag;
> > -    DiagnosticsJSON +=
> > -        R"({"range":)" + Range::unparse(Diag.range) +
> > -        R"(,"severity":)" + std::to_string(Diag.severity) +
> > -        R"(,"message":")" + llvm::yaml::escape(Diag.message) +
> > -        R"("},)";
> > -
> > +    DiagnosticsJSON.push_back(json::obj{
> > +        {"range", Diag.range},
> > +        {"severity", Diag.severity},
> > +        {"message", Diag.message},
> > +    });
> >      // We convert to Replacements to become independent of the
> SourceManager.
> >      auto &FixItsForDiagnostic = LocalFixIts[Diag];
> >      std::copy(DiagWithFixes.FixIts.begin(), DiagWithFixes.FixIts.end(),
> > @@ -295,10 +283,13 @@ void ClangdLSPServer::onDiagnosticsReady
> >    }
> >
> >    // Publish diagnostics.
> > -  if (!DiagnosticsJSON.empty())
> > -    DiagnosticsJSON.pop_back(); // Drop trailing comma.
> > -  Out.writeMessage(
> > -
> > R"({"jsonrpc":"2.0","method":"textDocument/publishDiagnostic
> s","params":{"uri"
> > :")" +
> > -      URI::fromFile(File).uri + R"(","diagnostics":[)" +
> DiagnosticsJSON +
> > -      R"(]}})");
> > +  Out.writeMessage(json::obj{
> > +      {"jsonrpc", "2.0"},
> > +      {"method", "textDocument/publishDiagnostics"},
> > +      {"params",
> > +       json::obj{
> > +           {"uri", URI::fromFile(File)},
> > +           {"diagnostics", std::move(DiagnosticsJSON)},
> > +       }},
> > +  });
> >  }
> >
> > Added: clang-tools-extra/trunk/clangd/JSONExpr.cpp
> > URL: http://llvm.org/viewvc/llvm-project/clang-tools-
> > extra/trunk/clangd/JSONExpr.cpp?rev=317486&view=auto
> > ============================================================
> ==================
> > --- clang-tools-extra/trunk/clangd/JSONExpr.cpp (added)
> > +++ clang-tools-extra/trunk/clangd/JSONExpr.cpp Mon Nov  6 07:40:30 2017
> > @@ -0,0 +1,216 @@
> > +#include "JSONExpr.h"
> > +
> > +#include "llvm/Support/Format.h"
> > +
> > +namespace clang {
> > +namespace clangd {
> > +namespace json {
> > +using namespace llvm;
> > +
> > +void Expr::copyFrom(const Expr &M) {
> > +  Type = M.Type;
> > +  switch (Type) {
> > +  case T_Null:
> > +  case T_Boolean:
> > +  case T_Number:
> > +    memcpy(Union.buffer, M.Union.buffer, sizeof(Union.buffer));
> > +    break;
> > +  case T_StringRef:
> > +    create<StringRef>(M.as<StringRef>());
> > +    break;
> > +  case T_String:
> > +    create<std::string>(M.as<std::string>());
> > +    break;
> > +  case T_Object:
> > +    create<Object>(M.as<Object>());
> > +    break;
> > +  case T_Array:
> > +    create<Array>(M.as<Array>());
> > +    break;
> > +  }
> > +}
> > +
> > +void Expr::moveFrom(const Expr &&M) {
> > +  Type = M.Type;
> > +  switch (Type) {
> > +  case T_Null:
> > +  case T_Boolean:
> > +  case T_Number:
> > +    memcpy(Union.buffer, M.Union.buffer, sizeof(Union.buffer));
> > +    break;
> > +  case T_StringRef:
> > +    create<StringRef>(M.as<StringRef>());
> > +    break;
> > +  case T_String:
> > +    create<std::string>(std::move(M.as<std::string>()));
> > +    M.Type = T_Null;
> > +    break;
> > +  case T_Object:
> > +    create<Object>(std::move(M.as<Object>()));
> > +    M.Type = T_Null;
> > +    break;
> > +  case T_Array:
> > +    create<Array>(std::move(M.as<Array>()));
> > +    M.Type = T_Null;
> > +    break;
> > +  }
> > +}
> > +
> > +void Expr::destroy() {
> > +  switch (Type) {
> > +  case T_Null:
> > +  case T_Boolean:
> > +  case T_Number:
> > +    break;
> > +  case T_StringRef:
> > +    as<StringRef>().~StringRef();
> > +    break;
> > +  case T_String:
> > +    as<std::string>().~basic_string();
> > +    break;
> > +  case T_Object:
> > +    as<Object>().~Object();
> > +    break;
> > +  case T_Array:
> > +    as<Array>().~Array();
> > +    break;
> > +  }
> > +}
> > +
> > +} // namespace json
> > +} // namespace clangd
> > +} // namespace clang
> > +
> > +namespace {
> > +void quote(llvm::raw_ostream &OS, llvm::StringRef S) {
> > +  OS << '\"';
> > +  for (unsigned char C : S) {
> > +    if (C == 0x22 || C == 0x5C)
> > +      OS << '\\';
> > +    if (C >= 0x20) {
> > +      OS << C;
> > +      continue;
> > +    }
> > +    OS << '\\';
> > +    switch (C) {
> > +    // A few characters are common enough to make short escapes
> worthwhile.
> > +    case '\t':
> > +      OS << 't';
> > +      break;
> > +    case '\n':
> > +      OS << 'n';
> > +      break;
> > +    case '\r':
> > +      OS << 'r';
> > +      break;
> > +    default:
> > +      OS << 'u';
> > +      llvm::write_hex(OS, C, llvm::HexPrintStyle::Lower, 4);
> > +      break;
> > +    }
> > +  }
> > +  OS << '\"';
> > +}
> > +
> > +enum IndenterAction {
> > +  Indent,
> > +  Outdent,
> > +  Newline,
> > +  Space,
> > +};
> > +} // namespace
> > +
> > +// Prints JSON. The indenter can be used to control formatting.
> > +template <typename Indenter>
> > +void clang::clangd::json::Expr::print(raw_ostream &OS,
> > +                                      const Indenter &I) const {
> > +  switch (Type) {
> > +  case T_Null:
> > +    OS << "null";
> > +    break;
> > +  case T_Boolean:
> > +    OS << (as<bool>() ? "true" : "false");
> > +    break;
> > +  case T_Number:
> > +    OS << format("%g", as<double>());
> > +    break;
> > +  case T_StringRef:
> > +    quote(OS, as<StringRef>());
> > +    break;
> > +  case T_String:
> > +    quote(OS, as<std::string>());
> > +    break;
> > +  case T_Object: {
> > +    bool Comma = false;
> > +    OS << '{';
> > +    I(Indent);
> > +    for (const auto &P : as<Expr::Object>()) {
> > +      if (Comma)
> > +        OS << ',';
> > +      Comma = true;
> > +      I(Newline);
> > +      quote(OS, P.first);
> > +      OS << ':';
> > +      I(Space);
> > +      P.second.print(OS, I);
> > +    }
> > +    I(Outdent);
> > +    if (Comma)
> > +      I(Newline);
> > +    OS << '}';
> > +    break;
> > +  }
> > +  case T_Array: {
> > +    bool Comma = false;
> > +    OS << '[';
> > +    I(Indent);
> > +    for (const auto &E : as<Expr::Array>()) {
> > +      if (Comma)
> > +        OS << ',';
> > +      Comma = true;
> > +      I(Newline);
> > +      E.print(OS, I);
> > +    }
> > +    I(Outdent);
> > +    if (Comma)
> > +      I(Newline);
> > +    OS << ']';
> > +    break;
> > +  }
> > +  }
> > +}
> > +
> > +llvm::raw_ostream &clang::clangd::json::operator<<(raw_ostream &OS,
> > +                                                   const Expr &E) {
> > +  E.print(OS, [](IndenterAction A) { /*ignore*/ });
> > +  return OS;
> > +}
> > +
> > +void llvm::format_provider<clang::clangd::json::Expr>::format(
> > +    const clang::clangd::json::Expr &E, raw_ostream &OS, StringRef
> Options) {
> > +  if (Options.empty()) {
> > +    OS << E;
> > +    return;
> > +  }
> > +  unsigned IndentAmount = 0;
> > +  if (Options.getAsInteger(/*Radix=*/10, IndentAmount))
> > +    assert(false && "json::Expr format options should be an integer");
> > +  unsigned IndentLevel = 0;
> > +  E.print(OS, [&](IndenterAction A) {
> > +    switch (A) {
> > +    case Newline:
> > +      OS << '\n';
> > +      OS.indent(IndentLevel);
> > +      break;
> > +    case Space:
> > +      OS << ' ';
> > +      break;
> > +    case Indent:
> > +      IndentLevel += IndentAmount;
> > +      break;
> > +    case Outdent:
> > +      IndentLevel -= IndentAmount;
> > +      break;
> > +    };
> > +  });
> > +}
> >
> > Added: clang-tools-extra/trunk/clangd/JSONExpr.h
> > URL: http://llvm.org/viewvc/llvm-project/clang-tools-
> > extra/trunk/clangd/JSONExpr.h?rev=317486&view=auto
> > ============================================================
> ==================
> > --- clang-tools-extra/trunk/clangd/JSONExpr.h (added)
> > +++ clang-tools-extra/trunk/clangd/JSONExpr.h Mon Nov  6 07:40:30 2017
> > @@ -0,0 +1,247 @@
> > +//===--- JSONExpr.h - composable JSON expressions ---------------*- C++
> -*-
> > ===//
> > +//
> > +//                     The LLVM Compiler Infrastructure
> > +//
> > +// This file is distributed under the University of Illinois Open Source
> > +// License. See LICENSE.TXT for details.
> > +//
> > +//===------------------------------------------------------
> ---------------
> > ===//
> > +
> > +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_JSON_H
> > +#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_JSON_H
> > +
> > +#include <map>
> > +
> > +#include "llvm/ADT/SmallVector.h"
> > +#include "llvm/ADT/StringRef.h"
> > +#include "llvm/Support/FormatVariadic.h"
> > +#include "llvm/Support/raw_ostream.h"
> > +
> > +namespace clang {
> > +namespace clangd {
> > +namespace json {
> > +
> > +// An Expr is an opaque temporary JSON structure used to compose
> documents.
> > +// They can be copied, but should generally be moved.
> > +//
> > +// You can implicitly construct literals from:
> > +//   - strings: std::string, SmallString, formatv, StringRef, char*
> > +//              (char*, and StringRef are references, not copies!)
> > +//   - numbers
> > +//   - booleans
> > +//   - null: nullptr
> > +//   - arrays: {"foo", 42.0, false}
> > +//   - serializable things: any T with a T::unparse(const T&) -> Expr
> > +//
> > +// They can also be constructed from object/array helpers:
> > +//   - json::obj is a type like map<StringExpr, Expr>
> > +//   - json::ary is a type like vector<Expr>
> > +// These can be list-initialized, or used to build up collections in a
> loop.
> > +// json::ary(Collection) converts all items in a collection to Exprs.
> > +//
> > +// Exprs can be serialized to JSON:
> > +//   1) raw_ostream << Expr                    // Basic formatting.
> > +//   2) raw_ostream << formatv("{0}", Expr)    // Basic formatting.
> > +//   3) raw_ostream << formatv("{0:2}", Expr)  // Pretty-print with
> indent 2.
> > +class Expr {
> > +public:
> > +  class Object;
> > +  class ObjectKey;
> > +  class Array;
> > +
> > +  // It would be nice to have Expr() be null. But that would make {}
> null
> > too...
> > +  Expr(const Expr &M) { copyFrom(M); }
> > +  Expr(Expr &&M) { moveFrom(std::move(M)); }
> > +  // "cheating" move-constructor for moving from initializer_list.
> > +  Expr(const Expr &&M) { moveFrom(std::move(M)); }
> > +  Expr(std::initializer_list<Expr> Elements) : Expr(Array(Elements)) {}
> > +  Expr(Array &&Elements) : Type(T_Array) {
> > create<Array>(std::move(Elements)); }
> > +  Expr(Object &&Properties) : Type(T_Object) {
> > +    create<Object>(std::move(Properties));
> > +  }
> > +  // Strings: types with value semantics.
> > +  Expr(std::string &&V) : Type(T_String) {
> create<std::string>(std::move(V));
> > }
> > +  Expr(const std::string &V) : Type(T_String) { create<std::string>(V);
> }
> > +  Expr(const llvm::SmallVectorImpl<char> &V) : Type(T_String) {
> > +    create<std::string>(V.begin(), V.end());
> > +  }
> > +  Expr(const llvm::formatv_object_base &V) : Expr(V.str()){};
> > +  // Strings: types with reference semantics.
> > +  Expr(llvm::StringRef V) : Type(T_StringRef) {
> create<llvm::StringRef>(V); }
> > +  Expr(const char *V) : Type(T_StringRef) { create<llvm::StringRef>(V);
> }
> > +  Expr(std::nullptr_t) : Type(T_Null) {}
> > +  // Prevent implicit conversions to boolean.
> > +  template <typename T, typename = typename std::enable_if<
> > +                            std::is_same<T, bool>::value>::type>
> > +  Expr(T B) : Type(T_Boolean) {
> > +    create<bool>(B);
> > +  }
> > +  // Numbers: arithmetic types that are not boolean.
> > +  template <
> > +      typename T,
> > +      typename = typename std::enable_if<std::is_arithme
> tic<T>::value>::type,
> > +      typename = typename std::enable_if<std::integral_constant<
> > +          bool, !std::is_same<T, bool>::value>::value>::type>
> > +  Expr(T D) : Type(T_Number) {
> > +    create<double>(D);
> > +  }
> > +  // Types with a static T::unparse function returning an Expr.
> > +  // FIXME: should this be a free unparse() function found by ADL?
> > +  template <typename T,
> > +            typename = typename std::enable_if<std::is_same<
> > +                Expr, decltype(T::unparse(*(const T
> *)nullptr))>::value>>
> > +  Expr(const T &V) : Expr(T::unparse(V)) {}
> > +
> > +  Expr &operator=(const Expr &M) {
> > +    destroy();
> > +    copyFrom(M);
> > +    return *this;
> > +  }
> > +  Expr &operator=(Expr &&M) {
> > +    destroy();
> > +    moveFrom(std::move(M));
> > +    return *this;
> > +  }
> > +  ~Expr() { destroy(); }
> > +
> > +  friend llvm::raw_ostream &operator<<(llvm::raw_ostream &, const Expr
> &);
> > +
> > +private:
> > +  void destroy();
> > +  void copyFrom(const Expr &M);
> > +  // We allow moving from *const* Exprs, by marking all members as
> mutable!
> > +  // This hack is needed to support initializer-list syntax efficiently.
> > +  // (std::initializer_list<T> is a container of const T).
> > +  void moveFrom(const Expr &&M);
> > +
> > +  template <typename T, typename... U> void create(U &&... V) {
> > +    new (&as<T>()) T(std::forward<U>(V)...);
> > +  }
> > +  template <typename T> T &as() const {
> > +    return *reinterpret_cast<T *>(Union.buffer);
> > +  }
> > +
> > +  template <typename Indenter>
> > +  void print(llvm::raw_ostream &, const Indenter &) const;
> > +  friend struct llvm::format_provider<clang::clangd::json::Expr>;
> > +
> > +  enum ExprType : char {
> > +    T_Null,
> > +    T_Boolean,
> > +    T_Number,
> > +    T_StringRef,
> > +    T_String,
> > +    T_Object,
> > +    T_Array,
> > +  };
> > +  mutable ExprType Type;
> > +
> > +public:
> > +  // ObjectKey is a used to capture keys in Expr::Objects. It's like
> Expr
> > but:
> > +  //   - only strings are allowed
> > +  //   - it's copyable (for std::map)
> > +  //   - we're slightly more eager to copy, to allow efficient key
> compares
> > +  //   - it's optimized for the string literal case (Owned == nullptr)
> > +  class ObjectKey {
> > +  public:
> > +    ObjectKey(const char *S) : Data(S) {}
> > +    ObjectKey(llvm::StringRef S) : Data(S) {}
> > +    ObjectKey(std::string &&V)
> > +        : Owned(new std::string(std::move(V))), Data(*Owned) {}
> > +    ObjectKey(const std::string &V) : Owned(new std::string(V)),
> Data(*Owned)
> > {}
> > +    ObjectKey(const llvm::SmallVectorImpl<char> &V)
> > +        : ObjectKey(std::string(V.begin(), V.end())) {}
> > +    ObjectKey(const llvm::formatv_object_base &V) : ObjectKey(V.str())
> {}
> > +
> > +    ObjectKey(const ObjectKey &C) { *this = C; }
> > +    ObjectKey(ObjectKey &&C) = default;
> > +    ObjectKey &operator=(const ObjectKey &C) {
> > +      if (C.Owned) {
> > +        Owned.reset(new std::string(*C.Owned));
> > +        Data = *Owned;
> > +      } else {
> > +        Data = C.Data;
> > +      }
> > +      return *this;
> > +    }
> > +    ObjectKey &operator=(ObjectKey &&) = default;
> > +
> > +    operator llvm::StringRef() const { return Data; }
> > +
> > +    friend bool operator<(const ObjectKey &L, const ObjectKey &R) {
> > +      return L.Data < R.Data;
> > +    }
> > +
> > +    // "cheating" move-constructor for moving from initializer_list.
> > +    ObjectKey(const ObjectKey &&V) {
> > +      Owned = std::move(V.Owned);
> > +      Data = V.Data;
> > +    }
> > +
> > +  private:
> > +    mutable std::unique_ptr<std::string> Owned; // mutable for cheating.
> > +    llvm::StringRef Data;
> > +  };
> > +
> > +  class Object : public std::map<ObjectKey, Expr> {
> > +  public:
> > +    explicit Object() {}
> > +    // Use a custom struct for list-init, because pair forces extra
> copies.
> > +    struct KV;
> > +    explicit Object(std::initializer_list<KV> Properties);
> > +
> > +    // Allow [] as if Expr was default-constructible as null.
> > +    Expr &operator[](const ObjectKey &K) {
> > +      return emplace(K, Expr(nullptr)).first->second;
> > +    }
> > +    Expr &operator[](ObjectKey &&K) {
> > +      return emplace(std::move(K), Expr(nullptr)).first->second;
> > +    }
> > +  };
> > +
> > +  class Array : public std::vector<Expr> {
> > +  public:
> > +    explicit Array() {}
> > +    explicit Array(std::initializer_list<Expr> Elements) {
> > +      reserve(Elements.size());
> > +      for (const Expr &V : Elements)
> > +        emplace_back(std::move(V));
> > +    };
> > +    template <typename Collection> explicit Array(const Collection &C) {
> > +      for (const auto &V : C)
> > +        emplace_back(V);
> > +    }
> > +  };
> > +
> > +private:
> > +  mutable llvm::AlignedCharArrayUnion<bool, double, llvm::StringRef,
> > +                                      std::string, Array, Object>
> > +      Union;
> > +};
> > +
> > +struct Expr::Object::KV {
> > +  ObjectKey K;
> > +  Expr V;
> > +};
> > +
> > +inline Expr::Object::Object(std::initializer_list<KV> Properties) {
> > +  for (const auto &P : Properties)
> > +    emplace(std::move(P.K), std::move(P.V));
> > +}
> > +
> > +// Give Expr::{Object,Array} more convenient names for literal use.
> > +using obj = Expr::Object;
> > +using ary = Expr::Array;
> > +
> > +} // namespace json
> > +} // namespace clangd
> > +} // namespace clang
> > +
> > +namespace llvm {
> > +template <> struct format_provider<clang::clangd::json::Expr> {
> > +  static void format(const clang::clangd::json::Expr &, raw_ostream &,
> > +                     StringRef);
> > +};
> > +} // namespace llvm
> > +
> > +#endif
> >
> > Modified: clang-tools-extra/trunk/clangd/JSONRPCDispatcher.cpp
> > URL: http://llvm.org/viewvc/llvm-project/clang-tools-
> > extra/trunk/clangd/JSONRPCDispatcher.cpp?rev=317486&r1=31748
> 5&r2=317486&view=d
> > iff
> > ============================================================
> ==================
> > --- clang-tools-extra/trunk/clangd/JSONRPCDispatcher.cpp (original)
> > +++ clang-tools-extra/trunk/clangd/JSONRPCDispatcher.cpp Mon Nov  6
> 07:40:30
> > 2017
> > @@ -8,6 +8,7 @@
> >  //===-------------------------------------------------------
> ---------------
> > ===//
> >
> >  #include "JSONRPCDispatcher.h"
> > +#include "JSONExpr.h"
> >  #include "ProtocolHandlers.h"
> >  #include "Trace.h"
> >  #include "llvm/ADT/SmallString.h"
> > @@ -18,17 +19,22 @@
> >  using namespace clang;
> >  using namespace clangd;
> >
> > -void JSONOutput::writeMessage(const Twine &Message) {
> > -  llvm::SmallString<128> Storage;
> > -  StringRef M = Message.toStringRef(Storage);
> > +void JSONOutput::writeMessage(const json::Expr &Message) {
> > +  std::string S;
> > +  llvm::raw_string_ostream OS(S);
> > +  if (Pretty)
> > +    OS << llvm::formatv("{0:2}", Message);
> > +  else
> > +    OS << Message;
> > +  OS.flush();
> >
> >    std::lock_guard<std::mutex> Guard(StreamMutex);
> >    // Log without headers.
> > -  Logs << "--> " << M << '\n';
> > +  Logs << "--> " << S << '\n';
> >    Logs.flush();
> >
> >    // Emit message with header.
> > -  Outs << "Content-Length: " << M.size() << "\r\n\r\n" << M;
> > +  Outs << "Content-Length: " << S.size() << "\r\n\r\n" << S;
> >    Outs.flush();
> >  }
> >
> > @@ -47,30 +53,38 @@ void JSONOutput::mirrorInput(const Twine
> >    InputMirror->flush();
> >  }
> >
> > -void RequestContext::reply(const llvm::Twine &Result) {
> > -  if (ID.empty()) {
> > +void RequestContext::reply(json::Expr &&Result) {
> > +  if (!ID) {
> >      Out.log("Attempted to reply to a notification!\n");
> >      return;
> >    }
> > -  Out.writeMessage(llvm::Twine(R"({"jsonrpc":"2.0","id":)") + ID +
> > -                   R"(,"result":)" + Result + "}");
> > +  Out.writeMessage(json::obj{
> > +      {"jsonrpc", "2.0"},
> > +      {"id", *ID},
> > +      {"result", std::move(Result)},
> > +  });
> >  }
> >
> >  void RequestContext::replyError(int code, const llvm::StringRef
> &Message) {
> >    Out.log("Error " + llvm::Twine(code) + ": " + Message + "\n");
> > -  if (!ID.empty()) {
> > -    Out.writeMessage(llvm::Twine(R"({"jsonrpc":"2.0","id":)") + ID +
> > -                     R"(,"error":{"code":)" + llvm::Twine(code) +
> > -                     R"(,"message":")" + llvm::yaml::escape(Message) +
> > -                     R"("}})");
> > +  if (ID) {
> > +    Out.writeMessage(json::obj{
> > +        {"jsonrpc", "2.0"},
> > +        {"id", *ID},
> > +        {"error", json::obj{{"code", code}, {"message", Message}}},
> > +    });
> >    }
> >  }
> >
> > -void RequestContext::call(StringRef Method, StringRef Params) {
> > +void RequestContext::call(StringRef Method, json::Expr &&Params) {
> >    // FIXME: Generate/Increment IDs for every request so that we can get
> > proper
> >    // replies once we need to.
> > -  Out.writeMessage(llvm::Twine(R"({"jsonrpc":"2.0","id":1,"method":")"
> +
> > -                               Method + R"(","params":)" + Params +
> R"(})"));
> > +  Out.writeMessage(json::obj{
> > +      {"jsonrpc", "2.0"},
> > +      {"id", 1},
> > +      {"method", Method},
> > +      {"params", std::move(Params)},
> > +  });
> >  }
> >
> >  void JSONRPCDispatcher::registerHandler(StringRef Method, Handler H) {
> > @@ -80,7 +94,7 @@ void JSONRPCDispatcher::registerHandler(
> >
> >  static void
> >  callHandler(const llvm::StringMap<JSONRPCDispatcher::Handler>
> &Handlers,
> > -            llvm::yaml::ScalarNode *Method, llvm::yaml::ScalarNode *Id,
> > +            llvm::yaml::ScalarNode *Method, llvm::Optional<json::Expr>
> ID,
> >              llvm::yaml::MappingNode *Params,
> >              const JSONRPCDispatcher::Handler &UnknownHandler, JSONOutput
> > &Out) {
> >    llvm::SmallString<64> MethodStorage;
> > @@ -88,7 +102,7 @@ callHandler(const llvm::StringMap<JSONRP
> >    auto I = Handlers.find(MethodStr);
> >    auto &Handler = I != Handlers.end() ? I->second : UnknownHandler;
> >    trace::Span Tracer(MethodStr);
> > -  Handler(RequestContext(Out, Id ? Id->getRawValue() : ""), Params);
> > +  Handler(RequestContext(Out, std::move(ID)), Params);
> >  }
> >
> >  bool JSONRPCDispatcher::call(StringRef Content, JSONOutput &Out) const
> {
> > @@ -106,7 +120,7 @@ bool JSONRPCDispatcher::call(StringRef C
> >    llvm::yaml::ScalarNode *Version = nullptr;
> >    llvm::yaml::ScalarNode *Method = nullptr;
> >    llvm::yaml::MappingNode *Params = nullptr;
> > -  llvm::yaml::ScalarNode *Id = nullptr;
> > +  llvm::Optional<json::Expr> ID;
> >    for (auto &NextKeyValue : *Object) {
> >      auto *KeyString =
> >          dyn_cast_or_null<llvm::yaml::ScalarNode>(NextKeyValue.getKey
> ());
> > @@ -127,7 +141,18 @@ bool JSONRPCDispatcher::call(StringRef C
> >      } else if (KeyValue == "method") {
> >        Method = dyn_cast<llvm::yaml::ScalarNode>(Value);
> >      } else if (KeyValue == "id") {
> > -      Id = dyn_cast<llvm::yaml::ScalarNode>(Value);
> > +      // ID may be either a string or a number.
> > +      if (auto *IdNode = dyn_cast<llvm::yaml::ScalarNode>(Value)) {
> > +        llvm::SmallString<32> S;
> > +        llvm::StringRef V = IdNode->getValue(S);
> > +        if (IdNode->getRawValue().startswith("\"")) {
> > +          ID.emplace(V.str());
> > +        } else {
> > +          double D;
> > +          if (!V.getAsDouble(D))
> > +            ID.emplace(D);
> > +        }
> > +      }
> >      } else if (KeyValue == "params") {
> >        if (!Method)
> >          return false;
> > @@ -136,7 +161,7 @@ bool JSONRPCDispatcher::call(StringRef C
> >        // because it will break clients that put the id after params. A
> > possible
> >        // fix would be to split the parsing and execution phases.
> >        Params = dyn_cast<llvm::yaml::MappingNode>(Value);
> > -      callHandler(Handlers, Method, Id, Params, UnknownHandler, Out);
> > +      callHandler(Handlers, Method, std::move(ID), Params,
> UnknownHandler,
> > Out);
> >        return true;
> >      } else {
> >        return false;
> > @@ -147,7 +172,7 @@ bool JSONRPCDispatcher::call(StringRef C
> >    // leftovers.
> >    if (!Method)
> >      return false;
> > -  callHandler(Handlers, Method, Id, nullptr, UnknownHandler, Out);
> > +  callHandler(Handlers, Method, std::move(ID), nullptr, UnknownHandler,
> Out);
> >
> >    return true;
> >  }
> >
> > Modified: clang-tools-extra/trunk/clangd/JSONRPCDispatcher.h
> > URL: http://llvm.org/viewvc/llvm-project/clang-tools-
> > extra/trunk/clangd/JSONRPCDispatcher.h?rev=317486&r1=317485&
> r2=317486&view=dif
> > f
> > ============================================================
> ==================
> > --- clang-tools-extra/trunk/clangd/JSONRPCDispatcher.h (original)
> > +++ clang-tools-extra/trunk/clangd/JSONRPCDispatcher.h Mon Nov  6
> 07:40:30
> > 2017
> > @@ -10,6 +10,7 @@
> >  #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_JSONRPCDISPATCHER_H
> >  #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_JSONRPCDISPATCHER_H
> >
> > +#include "JSONExpr.h"
> >  #include "Logger.h"
> >  #include "clang/Basic/LLVM.h"
> >  #include "llvm/ADT/SmallString.h"
> > @@ -26,11 +27,11 @@ namespace clangd {
> >  class JSONOutput : public Logger {
> >  public:
> >    JSONOutput(llvm::raw_ostream &Outs, llvm::raw_ostream &Logs,
> > -             llvm::raw_ostream *InputMirror = nullptr)
> > -      : Outs(Outs), Logs(Logs), InputMirror(InputMirror) {}
> > +             llvm::raw_ostream *InputMirror = nullptr, bool Pretty =
> false)
> > +      : Outs(Outs), Logs(Logs), InputMirror(InputMirror),
> Pretty(Pretty) {}
> >
> >    /// Emit a JSONRPC message.
> > -  void writeMessage(const Twine &Message);
> > +  void writeMessage(const json::Expr &Result);
> >
> >    /// Write to the logging stream.
> >    /// No newline is implicitly added. (TODO: we should fix this!)
> > @@ -45,6 +46,7 @@ private:
> >    llvm::raw_ostream &Outs;
> >    llvm::raw_ostream &Logs;
> >    llvm::raw_ostream *InputMirror;
> > +  bool Pretty;
> >
> >    std::mutex StreamMutex;
> >  };
> > @@ -52,18 +54,19 @@ private:
> >  /// Context object passed to handlers to allow replies.
> >  class RequestContext {
> >  public:
> > -  RequestContext(JSONOutput &Out, StringRef ID) : Out(Out), ID(ID) {}
> > +  RequestContext(JSONOutput &Out, llvm::Optional<json::Expr> ID)
> > +      : Out(Out), ID(std::move(ID)) {}
> >
> > -  /// Sends a successful reply. Result should be well-formed JSON.
> > -  void reply(const Twine &Result);
> > +  /// Sends a successful reply.
> > +  void reply(json::Expr &&Result);
> >    /// Sends an error response to the client, and logs it.
> >    void replyError(int code, const llvm::StringRef &Message);
> >    /// Sends a request to the client.
> > -  void call(llvm::StringRef Method, StringRef Params);
> > +  void call(llvm::StringRef Method, json::Expr &&Params);
> >
> >  private:
> >    JSONOutput &Out;
> > -  llvm::SmallString<64> ID; // Valid JSON, or empty for a notification.
> > +  llvm::Optional<json::Expr> ID;
> >  };
> >
> >  /// Main JSONRPC entry point. This parses the JSONRPC "header" and
> calls the
> >
> > Modified: clang-tools-extra/trunk/clangd/Protocol.cpp
> > URL: http://llvm.org/viewvc/llvm-project/clang-tools-
> > extra/trunk/clangd/Protocol.cpp?rev=317486&r1=317485&r2=317486&view=diff
> > ============================================================
> ==================
> > --- clang-tools-extra/trunk/clangd/Protocol.cpp (original)
> > +++ clang-tools-extra/trunk/clangd/Protocol.cpp Mon Nov  6 07:40:30 2017
> > @@ -63,7 +63,7 @@ URI URI::parse(llvm::yaml::ScalarNode *P
> >    return URI::fromUri(Param->getValue(Storage));
> >  }
> >
> > -std::string URI::unparse(const URI &U) { return "\"" + U.uri + "\""; }
> > +json::Expr URI::unparse(const URI &U) { return U.uri; }
> >
> >  llvm::Optional<TextDocumentIdentifier>
> >  TextDocumentIdentifier::parse(llvm::yaml::MappingNode *Params,
> > @@ -125,11 +125,11 @@ llvm::Optional<Position> Position::parse
> >    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;
> > +json::Expr Position::unparse(const Position &P) {
> > +  return json::obj{
> > +      {"line", P.line},
> > +      {"character", P.character},
> > +  };
> >  }
> >
> >  llvm::Optional<Range> Range::parse(llvm::yaml::MappingNode *Params,
> > @@ -165,20 +165,18 @@ llvm::Optional<Range> Range::parse(llvm:
> >    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_s
> tr(),
> > -      Position::unparse(P.end).c_str());
> > -  return Result;
> > +json::Expr Range::unparse(const Range &P) {
> > +  return json::obj{
> > +      {"start", P.start},
> > +      {"end", P.end},
> > +  };
> >  }
> >
> > -std::string Location::unparse(const Location &P) {
> > -  std::string Result;
> > -  llvm::raw_string_ostream(Result) << llvm::format(
> > -      R"({"uri": %s, "range": %s})", URI::unparse(P.uri).c_str(),
> > -      Range::unparse(P.range).c_str());
> > -  return Result;
> > +json::Expr Location::unparse(const Location &P) {
> > +  return json::obj{
> > +      {"uri", P.uri},
> > +      {"range", P.range},
> > +  };
> >  }
> >
> >  llvm::Optional<TextDocumentItem>
> > @@ -279,25 +277,11 @@ llvm::Optional<TextEdit> TextEdit::parse
> >    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;
> > -}
> > -
> > -std::string TextEdit::unparse(const std::vector<TextEdit> &TextEdits) {
> > -  // Fuse all edits into one big JSON array.
> > -  std::string Edits;
> > -  for (auto &TE : TextEdits) {
> > -    Edits += TextEdit::unparse(TE);
> > -    Edits += ',';
> > -  }
> > -  if (!Edits.empty())
> > -    Edits.pop_back();
> > -
> > -  return "[" + Edits + "]";
> > +json::Expr TextEdit::unparse(const TextEdit &P) {
> > +  return json::obj{
> > +      {"range", P.range},
> > +      {"newText", P.newText},
> > +  };
> >  }
> >
> >  namespace {
> > @@ -611,11 +595,11 @@ FormattingOptions::parse(llvm::yaml::Map
> >    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;
> > +json::Expr FormattingOptions::unparse(const FormattingOptions &P) {
> > +  return json::obj{
> > +      {"tabSize", P.tabSize},
> > +      {"insertSpaces", P.insertSpaces},
> > +  };
> >  }
> >
> >  llvm::Optional<DocumentRangeFormattingParams>
> > @@ -982,28 +966,18 @@ ExecuteCommandParams::parse(llvm::yaml::
> >    return Result;
> >  }
> >
> > -std::string WorkspaceEdit::unparse(const WorkspaceEdit &WE) {
> > -  std::string Changes;
> > -  for (auto &Change : *WE.changes) {
> > -    Changes += llvm::formatv(R"("{0}": {1})", Change.first,
> > -                             TextEdit::unparse(Change.second));
> > -    Changes += ',';
> > -  }
> > -  if (!Changes.empty())
> > -    Changes.pop_back();
> > -
> > -  std::string Result;
> > -  llvm::raw_string_ostream(Result)
> > -      << llvm::format(R"({"changes": {%s}})", Changes.c_str());
> > -  return Result;
> > +json::Expr WorkspaceEdit::unparse(const WorkspaceEdit &WE) {
> > +  if (!WE.changes)
> > +    return json::obj{};
> > +  json::obj FileChanges;
> > +  for (auto &Change : *WE.changes)
> > +    FileChanges[Change.first] = json::ary(Change.second);
> > +  return json::obj{{"changes", std::move(FileChanges)}};
> >  }
> >
> > -std::string
> > +json::Expr
> >  ApplyWorkspaceEditParams::unparse(const ApplyWorkspaceEditParams
> &Params) {
> > -  std::string Result;
> > -  llvm::raw_string_ostream(Result) << llvm::format(
> > -      R"({"edit": %s})", WorkspaceEdit::unparse(Params.edit).c_str());
> > -  return Result;
> > +  return json::obj{{"edit", Params.edit}};
> >  }
> >
> >  llvm::Optional<TextDocumentPositionParams>
> > @@ -1040,96 +1014,57 @@ TextDocumentPositionParams::parse(llvm::
> >    return Result;
> >  }
> >
> > -std::string CompletionItem::unparse(const CompletionItem &CI) {
> > -  std::string Result = "{";
> > -  llvm::raw_string_ostream Os(Result);
> > +json::Expr CompletionItem::unparse(const CompletionItem &CI) {
> >    assert(!CI.label.empty() && "completion item label is required");
> > -  Os << R"("label":")" << llvm::yaml::escape(CI.label) << R"(",)";
> > +  json::obj Result{{"label", CI.label}};
> >    if (CI.kind != CompletionItemKind::Missing)
> > -    Os << R"("kind":)" << static_cast<int>(CI.kind) << R"(,)";
> > +    Result["kind"] = static_cast<int>(CI.kind);
> >    if (!CI.detail.empty())
> > -    Os << R"("detail":")" << llvm::yaml::escape(CI.detail) << R"(",)";
> > +    Result["detail"] = CI.detail;
> >    if (!CI.documentation.empty())
> > -    Os << R"("documentation":")" << llvm::yaml::escape(CI.document
> ation)
> > -       << R"(",)";
> > +    Result["documentation"] = CI.documentation;
> >    if (!CI.sortText.empty())
> > -    Os << R"("sortText":")" << llvm::yaml::escape(CI.sortText) <<
> R"(",)";
> > +    Result["sortText"] = CI.sortText;
> >    if (!CI.filterText.empty())
> > -    Os << R"("filterText":")" << llvm::yaml::escape(CI.filterText) <<
> > R"(",)";
> > +    Result["filterText"] = CI.filterText;
> >    if (!CI.insertText.empty())
> > -    Os << R"("insertText":")" << llvm::yaml::escape(CI.insertText) <<
> > R"(",)";
> > -  if (CI.insertTextFormat != InsertTextFormat::Missing) {
> > -    Os << R"("insertTextFormat":)" << static_cast<int>(CI.insertText
> Format)
> > -       << R"(,)";
> > -  }
> > +    Result["insertText"] = CI.insertText;
> > +  if (CI.insertTextFormat != InsertTextFormat::Missing)
> > +    Result["insertTextFormat"] = static_cast<int>(CI.insertTextFormat);
> >    if (CI.textEdit)
> > -    Os << R"("textEdit":)" << TextEdit::unparse(*CI.textEdit) << ',';
> > -  if (!CI.additionalTextEdits.empty()) {
> > -    Os << R"("additionalTextEdits":[)";
> > -    for (const auto &Edit : CI.additionalTextEdits)
> > -      Os << TextEdit::unparse(Edit) << ",";
> > -    Os.flush();
> > -    // The list additionalTextEdits is guaranteed nonempty at this
> point.
> > -    // Replace the trailing comma with right brace.
> > -    Result.back() = ']';
> > -  }
> > -  Os.flush();
> > -  // Label is required, so Result is guaranteed to have a trailing
> comma.
> > -  Result.back() = '}';
> > -  return Result;
> > +    Result["textEdit"] = *CI.textEdit;
> > +  if (!CI.additionalTextEdits.empty())
> > +    Result["additionalTextEdits"] = json::ary(CI.additionalTextEdits);
> > +  return std::move(Result);
> >  }
> >
> > -std::string ParameterInformation::unparse(const ParameterInformation
> &PI) {
> > -  std::string Result = "{";
> > -  llvm::raw_string_ostream Os(Result);
> > +json::Expr ParameterInformation::unparse(const ParameterInformation
> &PI) {
> >    assert(!PI.label.empty() && "parameter information label is
> required");
> > -  Os << R"("label":")" << llvm::yaml::escape(PI.label) << '\"';
> > +  json::obj Result{{"label", PI.label}};
> >    if (!PI.documentation.empty())
> > -    Os << R"(,"documentation":")" << llvm::yaml::escape(PI.document
> ation)
> > -       << '\"';
> > -  Os << '}';
> > -  Os.flush();
> > -  return Result;
> > +    Result["documentation"] = PI.documentation;
> > +  return std::move(Result);
> >  }
> >
> > -std::string SignatureInformation::unparse(const SignatureInformation
> &SI) {
> > -  std::string Result = "{";
> > -  llvm::raw_string_ostream Os(Result);
> > +json::Expr SignatureInformation::unparse(const SignatureInformation
> &SI) {
> >    assert(!SI.label.empty() && "signature information label is
> required");
> > -  Os << R"("label":")" << llvm::yaml::escape(SI.label) << '\"';
> > +  json::obj Result{
> > +      {"label", SI.label},
> > +      {"parameters", json::ary(SI.parameters)},
> > +  };
> >    if (!SI.documentation.empty())
> > -    Os << R"(,"documentation":")" << llvm::yaml::escape(SI.document
> ation)
> > -       << '\"';
> > -  Os << R"(,"parameters":[)";
> > -  for (const auto &Parameter : SI.parameters) {
> > -    Os << ParameterInformation::unparse(Parameter) << ',';
> > -  }
> > -  Os.flush();
> > -  if (SI.parameters.empty())
> > -    Result.push_back(']');
> > -  else
> > -    Result.back() = ']'; // Replace the last `,` with an `]`.
> > -  Result.push_back('}');
> > -  return Result;
> > +    Result["documentation"] = SI.documentation;
> > +  return std::move(Result);
> >  }
> >
> > -std::string SignatureHelp::unparse(const SignatureHelp &SH) {
> > -  std::string Result = "{";
> > -  llvm::raw_string_ostream Os(Result);
> > +json::Expr SignatureHelp::unparse(const SignatureHelp &SH) {
> >    assert(SH.activeSignature >= 0 &&
> >           "Unexpected negative value for number of active signatures.");
> >    assert(SH.activeParameter >= 0 &&
> >           "Unexpected negative value for active parameter index");
> > -  Os << R"("activeSignature":)" << SH.activeSignature
> > -     << R"(,"activeParameter":)" << SH.activeParameter <<
> > R"(,"signatures":[)";
> > -  for (const auto &Signature : SH.signatures) {
> > -    Os << SignatureInformation::unparse(Signature) << ',';
> > -  }
> > -  Os.flush();
> > -  if (SH.signatures.empty())
> > -    Result.push_back(']');
> > -  else
> > -    Result.back() = ']'; // Replace the last `,` with an `]`.
> > -  Result.push_back('}');
> > -  return Result;
> > +  return json::obj{
> > +      {"activeSignature", SH.activeSignature},
> > +      {"activeParameter", SH.activeParameter},
> > +      {"signatures", json::ary(SH.signatures)},
> > +  };
> >  }
> >
> > Modified: clang-tools-extra/trunk/clangd/Protocol.h
> > URL: http://llvm.org/viewvc/llvm-project/clang-tools-
> > extra/trunk/clangd/Protocol.h?rev=317486&r1=317485&r2=317486&view=diff
> > ============================================================
> ==================
> > --- clang-tools-extra/trunk/clangd/Protocol.h (original)
> > +++ clang-tools-extra/trunk/clangd/Protocol.h Mon Nov  6 07:40:30 2017
> > @@ -21,6 +21,7 @@
> >  #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_PROTOCOL_H
> >  #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_PROTOCOL_H
> >
> > +#include "JSONExpr.h"
> >  #include "llvm/ADT/Optional.h"
> >  #include "llvm/Support/YAMLParser.h"
> >  #include <string>
> > @@ -39,7 +40,7 @@ struct URI {
> >    static URI fromFile(llvm::StringRef file);
> >
> >    static URI parse(llvm::yaml::ScalarNode *Param);
> > -  static std::string unparse(const URI &U);
> > +  static json::Expr unparse(const URI &U);
> >
> >    friend bool operator==(const URI &LHS, const URI &RHS) {
> >      return LHS.uri == RHS.uri;
> > @@ -80,7 +81,7 @@ struct Position {
> >
> >    static llvm::Optional<Position> parse(llvm::yaml::MappingNode *Params,
> >                                          clangd::Logger &Logger);
> > -  static std::string unparse(const Position &P);
> > +  static json::Expr unparse(const Position &P);
> >  };
> >
> >  struct Range {
> > @@ -99,7 +100,7 @@ struct Range {
> >
> >    static llvm::Optional<Range> parse(llvm::yaml::MappingNode *Params,
> >                                       clangd::Logger &Logger);
> > -  static std::string unparse(const Range &P);
> > +  static json::Expr unparse(const Range &P);
> >  };
> >
> >  struct Location {
> > @@ -119,7 +120,7 @@ struct Location {
> >      return std::tie(LHS.uri, LHS.range) < std::tie(RHS.uri, RHS.range);
> >    }
> >
> > -  static std::string unparse(const Location &P);
> > +  static json::Expr unparse(const Location &P);
> >  };
> >
> >  struct Metadata {
> > @@ -140,8 +141,7 @@ struct TextEdit {
> >
> >    static llvm::Optional<TextEdit> parse(llvm::yaml::MappingNode *Params,
> >                                          clangd::Logger &Logger);
> > -  static std::string unparse(const TextEdit &P);
> > -  static std::string unparse(const std::vector<TextEdit> &TextEdits);
> > +  static json::Expr unparse(const TextEdit &P);
> >  };
> >
> >  struct TextDocumentItem {
> > @@ -283,7 +283,7 @@ struct FormattingOptions {
> >
> >    static llvm::Optional<FormattingOptions>
> >    parse(llvm::yaml::MappingNode *Params, clangd::Logger &Logger);
> > -  static std::string unparse(const FormattingOptions &P);
> > +  static json::Expr unparse(const FormattingOptions &P);
> >  };
> >
> >  struct DocumentRangeFormattingParams {
> > @@ -392,7 +392,7 @@ struct WorkspaceEdit {
> >
> >    static llvm::Optional<WorkspaceEdit> parse(llvm::yaml::MappingNode
> *Params,
> >                                               clangd::Logger &Logger);
> > -  static std::string unparse(const WorkspaceEdit &WE);
> > +  static json::Expr unparse(const WorkspaceEdit &WE);
> >  };
> >
> >  /// Exact commands are not specified in the protocol so we define the
> > @@ -420,7 +420,7 @@ struct ExecuteCommandParams {
> >
> >  struct ApplyWorkspaceEditParams {
> >    WorkspaceEdit edit;
> > -  static std::string unparse(const ApplyWorkspaceEditParams &Params);
> > +  static json::Expr unparse(const ApplyWorkspaceEditParams &Params);
> >  };
> >
> >  struct TextDocumentPositionParams {
> > @@ -527,7 +527,7 @@ struct CompletionItem {
> >    //
> >    // data?: any - A data entry field that is preserved on a completion
> item
> >    //              between a completion and a completion resolve request.
> > -  static std::string unparse(const CompletionItem &P);
> > +  static json::Expr unparse(const CompletionItem &P);
> >  };
> >
> >  /// A single parameter of a particular signature.
> > @@ -539,7 +539,7 @@ struct ParameterInformation {
> >    /// The documentation of this parameter. Optional.
> >    std::string documentation;
> >
> > -  static std::string unparse(const ParameterInformation &);
> > +  static json::Expr unparse(const ParameterInformation &);
> >  };
> >
> >  /// Represents the signature of something callable.
> > @@ -554,7 +554,7 @@ struct SignatureInformation {
> >    /// The parameters of this signature.
> >    std::vector<ParameterInformation> parameters;
> >
> > -  static std::string unparse(const SignatureInformation &);
> > +  static json::Expr unparse(const SignatureInformation &);
> >  };
> >
> >  /// Represents the signature of a callable.
> > @@ -569,7 +569,7 @@ struct SignatureHelp {
> >    /// The active parameter of the active signature.
> >    int activeParameter = 0;
> >
> > -  static std::string unparse(const SignatureHelp &);
> > +  static json::Expr unparse(const SignatureHelp &);
> >  };
> >
> >  } // namespace clangd
> >
> > Modified: clang-tools-extra/trunk/clangd/tool/ClangdMain.cpp
> > URL: http://llvm.org/viewvc/llvm-project/clang-tools-
> > extra/trunk/clangd/tool/ClangdMain.cpp?rev=317486&r1=317485&
> r2=317486&view=dif
> > f
> > ============================================================
> ==================
> > --- clang-tools-extra/trunk/clangd/tool/ClangdMain.cpp (original)
> > +++ clang-tools-extra/trunk/clangd/tool/ClangdMain.cpp Mon Nov  6
> 07:40:30
> > 2017
> > @@ -41,6 +41,10 @@ static llvm::cl::opt<bool> EnableSnippet
> >          "Present snippet completions instead of plaintext completions"),
> >      llvm::cl::init(false));
> >
> > +static llvm::cl::opt<bool>
> > +    PrettyPrint("pretty", llvm::cl::desc("Pretty-print JSON output"),
> > +                llvm::cl::init(false));
> > +
> >  static llvm::cl::opt<bool> RunSynchronously(
> >      "run-synchronously",
> >      llvm::cl::desc("Parse on main thread. If set, -j is ignored"),
> > @@ -104,7 +108,8 @@ int main(int argc, char *argv[]) {
> >    llvm::raw_ostream &Outs = llvm::outs();
> >    llvm::raw_ostream &Logs = llvm::errs();
> >    JSONOutput Out(Outs, Logs,
> > -                 InputMirrorStream ? InputMirrorStream.getPointer() :
> > nullptr);
> > +                 InputMirrorStream ? InputMirrorStream.getPointer() :
> > nullptr,
> > +                 PrettyPrint);
> >
> >    // If --compile-commands-dir arg was invoked, check value and override
> > default
> >    // path.
> >
> > Modified: clang-tools-extra/trunk/test/clangd/authority-less-uri.test
> > URL: http://llvm.org/viewvc/llvm-project/clang-tools-
> > extra/trunk/test/clangd/authority-less-
> > uri.test?rev=317486&r1=317485&r2=317486&view=diff
> > ============================================================
> ==================
> > --- clang-tools-extra/trunk/test/clangd/authority-less-uri.test
> (original)
> > +++ clang-tools-extra/trunk/test/clangd/authority-less-uri.test Mon
> Nov  6
> > 07:40:30 2017
> > @@ -1,4 +1,4 @@
> > -# RUN: clangd -run-synchronously < %s | FileCheck %s
> > +# RUN: clangd -pretty -run-synchronously < %s | FileCheck
> -strict-whitespace
> > %s
> >  # It is absolutely vital that this file has CRLF line endings.
> >  #
> >  # Test authority-less URI
> > @@ -15,22 +15,34 @@ Content-Length: 146
> >
> > {"jsonrpc":"2.0","id":1,"method":"textDocument/completion","
> params":{"textDocu
> > ment":{"uri":"file:/main.cpp"},"position":{"line":3,"character":5}}}
> >  # Test authority-less URI
> >  #
> > -# CHECK: {"jsonrpc":"2.0","id":1,"result":[
> > -# CHECK-DAG:
> > {"label":"a","kind":5,"detail":"int","sortText":"000035a","f
> ilterText":"a","in
> > sertText":"a","insertTextFormat":1}
> > -# CHECK: ]}
> > -
> > +#      CHECK:  "id": 1,
> > +# CHECK-NEXT:  "jsonrpc": "2.0",
> > +# CHECK-NEXT:  "result": [
> > +#      CHECK:      "filterText": "fake",
> > +# CHECK-NEXT:      "insertText": "fake",
> > +# CHECK-NEXT:      "insertTextFormat": 1,
> > +# CHECK-NEXT:      "kind": 7,
> > +# CHECK-NEXT:      "label": "fake::",
> > +# CHECK-NEXT:      "sortText": "000075fake"
> > +#      CHECK:  ]
> >  Content-Length: 172
> >
> >
> > {"jsonrpc":"2.0","id":2,"method":"textDocument/completion","
> params":{"textDocu
> > ment":{"uri":"file:///main.cpp"},"uri":"file:///main.cpp","p
> osition":{"line":3
> > ,"character":5}}}
> >  # Test params parsing in the presence of a 1.x-compatible client
> (inlined
> > "uri")
> >  #
> > -# CHECK: {"jsonrpc":"2.0","id":2,"result":[
> > -# CHECK-DAG:
> > {"label":"a","kind":5,"detail":"int","sortText":"000035a","f
> ilterText":"a","in
> > sertText":"a","insertTextFormat":1}
> > -# CHECK: ]}
> > +#      CHECK:  "id": 2,
> > +# CHECK-NEXT:  "jsonrpc": "2.0",
> > +# CHECK-NEXT:  "result": [
> > +#      CHECK:      "filterText": "fake",
> > +# CHECK-NEXT:      "insertText": "fake",
> > +# CHECK-NEXT:      "insertTextFormat": 1,
> > +# CHECK-NEXT:      "kind": 7,
> > +# CHECK-NEXT:      "label": "fake::",
> > +# CHECK-NEXT:      "sortText": "000075fake"
> > +#      CHECK:  ]
> >  Content-Length: 44
> >
> >  {"jsonrpc":"2.0","id":3,"method":"shutdown"}
> > -# CHECK: {"jsonrpc":"2.0","id":3,"result":null}
> >  Content-Length: 33
> >
> >  {"jsonrpc":"2.0":"method":"exit"}
> >
> > Modified: clang-tools-extra/trunk/test/clangd/completion-items-kinds.t
> est
> > URL: http://llvm.org/viewvc/llvm-project/clang-tools-
> > extra/trunk/test/clangd/completion-items-
> > kinds.test?rev=317486&r1=317485&r2=317486&view=diff
> > ============================================================
> ==================
> > --- clang-tools-extra/trunk/test/clangd/completion-items-kinds.test
> (original)
> > +++ clang-tools-extra/trunk/test/clangd/completion-items-kinds.test Mon
> Nov  6
> > 07:40:30 2017
> > @@ -11,27 +11,26 @@ Content-Length: 148
> >
> >
> > {"jsonrpc":"2.0","id":1,"method":"textDocument/completion","
> params":{"textDocu
> > ment":{"uri":"file:///main.cpp"},"position":{"line":4,"character":7}}}
> >  Content-Length: 58
> > -# CHECK: {"jsonrpc":"2.0","id":1,"result":[
> > +# CHECK: {"id":1,"jsonrpc":"2.0","result":[
> >  #
> >  # Keyword
> > -# CHECK-DAG:
> > {"label":"int","kind":14,"sortText":"000050int","filterText"
> :"int","insertText
> > ":"int","insertTextFormat":1}
> > +# CHECK-DAG:
> > {"filterText":"int","insertText":"int","insertTextFormat":1,
> "kind":14,"label":
> > "int","sortText":"000050int"}
> >  #
> >  # Code pattern
> > -# CHECK-DAG:
> > {"label":"static_cast<type>(expression)","kind":15,"sortText
> ":"000040static_ca
> > st","filterText":"static_cast","insertText":"static_cast<${1
> :type}>(${2:expres
> > sion})","insertTextFormat":2}
> > +# CHECK-DAG:
> > {"filterText":"static_cast","insertText":"static_cast<${1:ty
> pe}>(${2:expressio
> > n})","insertTextFormat":2,"kind":15,"label":"static_cast<typ
> e>(expression)","s
> > ortText":"000040static_cast"}
> >  #
> >  # Struct
> > -# CHECK-DAG:
> > {"label":"Struct","kind":7,"sortText":"000050Struct","filter
> Text":"Struct","in
> > sertText":"Struct","insertTextFormat":1}
> > +# CHECK-DAG:
> > {"filterText":"Struct","insertText":"Struct","insertTextForm
> at":1,"kind":7,"la
> > bel":"Struct","sortText":"000050Struct"}
> >  #
> >  # Macro
> > -# CHECK-DAG:
> > {"label":"MACRO","kind":1,"sortText":"000070MACRO","filterTe
> xt":"MACRO","inser
> > tText":"MACRO","insertTextFormat":1}
> > +# CHECK-DAG:
> > {"filterText":"MACRO","insertText":"MACRO","insertTextFormat
> ":1,"kind":1,"labe
> > l":"MACRO","sortText":"000070MACRO"}
> >  #
> >  # Variable
> > -# CHECK-DAG:
> > {"label":"variable","kind":6,"detail":"int","sortText":"0000
> 12variable","filte
> > rText":"variable","insertText":"variable","insertTextFormat":1}
> > +# CHECK-DAG:
> > {"detail":"int","filterText":"variable","insertText":"variab
> le","insertTextFor
> > mat":1,"kind":6,"label":"variable","sortText":"000012variable"}
> >  #
> >  # Function
> > -# CHECK-DAG:
> > {"label":"function()","kind":3,"detail":"int","sortText":"00
> 0012function","fil
> > terText":"function","insertText":"function()","insertTextFormat":1}
> > +# CHECK-DAG:
> > {"detail":"int","filterText":"function","insertText":"functi
> on()","insertTextF
> > ormat":1,"kind":3,"label":"function()","sortText":"000012function"}
> >  #
> > -#
> > -# CHECK: ]}
> > +# CHECK-SAME: ]}
> >
> >  {"jsonrpc":"2.0","id":3,"method":"shutdown","params":null}
> >
> > Modified: clang-tools-extra/trunk/test/clangd/completion-priorities.test
> > URL: http://llvm.org/viewvc/llvm-project/clang-tools-
> > extra/trunk/test/clangd/completion-
> > priorities.test?rev=317486&r1=317485&r2=317486&view=diff
> > ============================================================
> ==================
> > --- clang-tools-extra/trunk/test/clangd/completion-priorities.test
> (original)
> > +++ clang-tools-extra/trunk/test/clangd/completion-priorities.test Mon
> Nov  6
> > 07:40:30 2017
> > @@ -16,25 +16,24 @@ Content-Length: 151
> >  # The order of results returned by codeComplete seems to be
> >  # nondeterministic, so we check regardless of order.
> >  #
> > -# CHECK: {"jsonrpc":"2.0","id":2,"result":[
> > -# CHECK-DAG:
> > {"label":"pub()","kind":2,"detail":"void","sortText":"000034
> pub","filterText":
> > "pub","insertText":"pub","insertTextFormat":1}
> > -# CHECK-DAG:
> > {"label":"prot()","kind":2,"detail":"void","sortText":"00003
> 4prot","filterText
> > ":"prot","insertText":"prot","insertTextFormat":1}
> > -# CHECK-DAG:
> > {"label":"priv()","kind":2,"detail":"void","sortText":"00003
> 4priv","filterText
> > ":"priv","insertText":"priv","insertTextFormat":1}
> > -# CHECK: ]}
> > +# CHECK: {"id":2,"jsonrpc":"2.0","result":[
> > +# CHECK-DAG:
> > {"detail":"void","filterText":"pub","insertText":"pub","inse
> rtTextFormat":1,"k
> > ind":2,"label":"pub()","sortText":"000034pub"}
> > +# CHECK-DAG:
> > {"detail":"void","filterText":"prot","insertText":"prot","in
> sertTextFormat":1,
> > "kind":2,"label":"prot()","sortText":"000034prot"}
> > +# CHECK-DAG:
> > {"detail":"void","filterText":"priv","insertText":"priv","in
> sertTextFormat":1,
> > "kind":2,"label":"priv()","sortText":"000034priv"}
> > +# CHECK-SAME: ]}
> >
> >  Content-Length: 151
> >
> >
> > {"jsonrpc":"2.0","id":3,"method":"textDocument/completion","
> params":{"textDocu
> > ment":{"uri":"file:///main.cpp"},"position":{"line":17,"character":4}}}
> > -# CHECK: {"jsonrpc":"2.0","id":3,"result":[
> > -# CHECK-DAG:
> > {"label":"pub()","kind":2,"detail":"void","sortText":"000034
> pub","filterText":
> > "pub","insertText":"pub","insertTextFormat":1}
> > -# CHECK-DAG:
> > {"label":"prot()","kind":2,"detail":"void","sortText":"20003
> 4prot","filterText
> > ":"prot","insertText":"prot","insertTextFormat":1}
> > -# CHECK-DAG:
> > {"label":"priv()","kind":2,"detail":"void","sortText":"20003
> 4priv","filterText
> > ":"priv","insertText":"priv","insertTextFormat":1}
> > -# CHECK: ]}
> > +# CHECK: {"id":3,"jsonrpc":"2.0","result":[
> > +# CHECK-DAG:
> > {"detail":"void","filterText":"pub","insertText":"pub","inse
> rtTextFormat":1,"k
> > ind":2,"label":"pub()","sortText":"000034pub"}
> > +# CHECK-DAG:
> > {"detail":"void","filterText":"prot","insertText":"prot","in
> sertTextFormat":1,
> > "kind":2,"label":"prot()","sortText":"200034prot"}
> > +# CHECK-DAG:
> > {"detail":"void","filterText":"priv","insertText":"priv","in
> sertTextFormat":1,
> > "kind":2,"label":"priv()","sortText":"200034priv"}
> > +# CHECK-SAME: ]}
> >
> >  Content-Length: 58
> >
> >  {"jsonrpc":"2.0","id":4,"method":"shutdown","params":null}
> > -# CHECK: {"jsonrpc":"2.0","id":4,"result":null}
> >  Content-Length: 33
> >
> >  {"jsonrpc":"2.0":"method":"exit"}
> >
> > Modified: clang-tools-extra/trunk/test/clangd/completion-qualifiers.test
> > URL: http://llvm.org/viewvc/llvm-project/clang-tools-
> > extra/trunk/test/clangd/completion-
> > qualifiers.test?rev=317486&r1=317485&r2=317486&view=diff
> > ============================================================
> ==================
> > --- clang-tools-extra/trunk/test/clangd/completion-qualifiers.test
> (original)
> > +++ clang-tools-extra/trunk/test/clangd/completion-qualifiers.test Mon
> Nov  6
> > 07:40:30 2017
> > @@ -8,15 +8,14 @@ Content-Length: 297
> >  Content-Length: 151
> >
> >
> > {"jsonrpc":"2.0","id":2,"method":"textDocument/completion","
> params":{"textDocu
> > ment":{"uri":"file:///main.cpp"},"position":{"line":11,"character":8}}}
> > -# CHECK: {"jsonrpc":"2.0","id":2,"result":[
> > -# CHECK-DAG: {"label":"foo()
> > const","kind":2,"detail":"int","sortText":"200035foo","filte
> rText":"foo","inse
> > rtText":"foo","insertTextFormat":1}
> > -# CHECK-DAG: {"label":"bar()
> > const","kind":2,"detail":"int","sortText":"000037bar","filte
> rText":"bar","inse
> > rtText":"bar","insertTextFormat":1}
> > -# CHECK-DAG: {"label":"Foo::foo()
> > const","kind":2,"detail":"int","sortText":"000037foo","filte
> rText":"foo","inse
> > rtText":"foo","insertTextFormat":1}
> > -# CHECK: ]}
> > +# CHECK: {"id":2,"jsonrpc":"2.0","result":[
> > +# CHECK-DAG:
> > {"detail":"int","filterText":"foo","insertText":"foo","inser
> tTextFormat":1,"ki
> > nd":2,"label":"foo() const","sortText":"200035foo"}
> > +# CHECK-DAG:
> > {"detail":"int","filterText":"bar","insertText":"bar","inser
> tTextFormat":1,"ki
> > nd":2,"label":"bar() const","sortText":"000037bar"}
> > +# CHECK-DAG:
> > {"detail":"int","filterText":"foo","insertText":"foo","inser
> tTextFormat":1,"ki
> > nd":2,"label":"Foo::foo() const","sortText":"000037foo"}
> > +# CHECK-SAME: ]}
> >  Content-Length: 44
> >
> >  {"jsonrpc":"2.0","id":4,"method":"shutdown"}
> > -# CHECK: {"jsonrpc":"2.0","id":4,"result":null}
> >  Content-Length: 33
> >
> >  {"jsonrpc":"2.0":"method":"exit"}
> >
> > Modified: clang-tools-extra/trunk/test/clangd/completion-snippet.test
> > URL: http://llvm.org/viewvc/llvm-project/clang-tools-
> > extra/trunk/test/clangd/completion-
> > snippet.test?rev=317486&r1=317485&r2=317486&view=diff
> > ============================================================
> ==================
> > --- clang-tools-extra/trunk/test/clangd/completion-snippet.test
> (original)
> > +++ clang-tools-extra/trunk/test/clangd/completion-snippet.test Mon
> Nov  6
> > 07:40:30 2017
> > @@ -15,27 +15,27 @@ Content-Length: 148
> >  # The order of results returned by codeComplete seems to be
> >  # nondeterministic, so we check regardless of order.
> >  #
> > -# CHECK: {"jsonrpc":"2.0","id":1,"result":[
> > -# CHECK-DAG:
> > {"label":"a","kind":5,"detail":"int","sortText":"000035a","f
> ilterText":"a","in
> > sertText":"a","insertTextFormat":1}
> > -# CHECK-DAG:
> > {"label":"bb","kind":5,"detail":"int","sortText":"000035bb",
> "filterText":"bb",
> > "insertText":"bb","insertTextFormat":1}
> > -# CHECK-DAG:
> > {"label":"ccc","kind":5,"detail":"int","sortText":"000035ccc
> ","filterText":"cc
> > c","insertText":"ccc","insertTextFormat":1}
> > -# CHECK-DAG: {"label":"operator=(const fake &)","kind":2,"detail":"fake
> > &","sortText":"000079operator=","filterText":"operator=","in
> sertText":"operato
> > r=(${1:const fake &})","insertTextFormat":2}
> > -# CHECK-DAG:
> > {"label":"~fake()","kind":4,"detail":"void","sortText":"0000
> 79~fake","filterTe
> > xt":"~fake","insertText":"~fake()","insertTextFormat":1}
> > -# CHECK-DAG: {"label":"f(int i, const float f)
> > const","kind":2,"detail":"int","sortText":"000035f","filterT
> ext":"f","insertTe
> > xt":"f(${1:int i}, ${2:const float f})","insertTextFormat":2}
> > -# CHECK: ]}
> > +# CHECK: {"id":1,"jsonrpc":"2.0","result":[
> > +# CHECK-DAG:
> > {"detail":"int","filterText":"a","insertText":"a","insertTex
> tFormat":1,"kind":
> > 5,"label":"a","sortText":"000035a"}
> > +# CHECK-DAG:
> > {"detail":"int","filterText":"bb","insertText":"bb","insertT
> extFormat":1,"kind
> > ":5,"label":"bb","sortText":"000035bb"}
> > +# CHECK-DAG:
> > {"detail":"int","filterText":"ccc","insertText":"ccc","inser
> tTextFormat":1,"ki
> > nd":5,"label":"ccc","sortText":"000035ccc"}
> > +# CHECK-DAG: {"detail":"fake
> > &","filterText":"operator=","insertText":"operator=(${1:const fake
> > &})","insertTextFormat":2,"kind":2,"label":"operator=(const fake
> > &)","sortText":"000079operator="}
> > +# CHECK-DAG:
> > {"detail":"void","filterText":"~fake","insertText":"~fake()"
> ,"insertTextFormat
> > ":1,"kind":4,"label":"~fake()","sortText":"000079~fake"}
> > +# CHECK-DAG: {"detail":"int","filterText":"f","insertText":"f(${1:int
> i},
> > ${2:const float f})","insertTextFormat":2,"kind":2,"label":"f(int i,
> const
> > float f) const","sortText":"000035f"}
> > +# CHECK-SAME: ]}
> >  Content-Length: 148
> >
> >
> > {"jsonrpc":"2.0","id":2,"method":"textDocument/completion","
> params":{"textDocu
> > ment":{"uri":"file:///main.cpp"},"position":{"line":3,"character":5}}}
> >  # Repeat the completion request, expect the same results.
> >  #
> > -# CHECK: {"jsonrpc":"2.0","id":2,"result":[
> > -# CHECK-DAG:
> > {"label":"a","kind":5,"detail":"int","sortText":"000035a","f
> ilterText":"a","in
> > sertText":"a","insertTextFormat":1}
> > -# CHECK-DAG:
> > {"label":"bb","kind":5,"detail":"int","sortText":"000035bb",
> "filterText":"bb",
> > "insertText":"bb","insertTextFormat":1}
> > -# CHECK-DAG:
> > {"label":"ccc","kind":5,"detail":"int","sortText":"000035ccc
> ","filterText":"cc
> > c","insertText":"ccc","insertTextFormat":1}
> > -# CHECK-DAG: {"label":"operator=(const fake &)","kind":2,"detail":"fake
> > &","sortText":"000079operator=","filterText":"operator=","in
> sertText":"operato
> > r=(${1:const fake &})","insertTextFormat":2}
> > -# CHECK-DAG:
> > {"label":"~fake()","kind":4,"detail":"void","sortText":"0000
> 79~fake","filterTe
> > xt":"~fake","insertText":"~fake()","insertTextFormat":1}
> > -# CHECK-DAG: {"label":"f(int i, const float f)
> > const","kind":2,"detail":"int","sortText":"000035f","filterT
> ext":"f","insertTe
> > xt":"f(${1:int i}, ${2:const float f})","insertTextFormat":2}
> > -# CHECK: ]}
> > +# CHECK: {"id":2,"jsonrpc":"2.0","result":[
> > +# CHECK-DAG:
> > {"detail":"int","filterText":"a","insertText":"a","insertTex
> tFormat":1,"kind":
> > 5,"label":"a","sortText":"000035a"}
> > +# CHECK-DAG:
> > {"detail":"int","filterText":"bb","insertText":"bb","insertT
> extFormat":1,"kind
> > ":5,"label":"bb","sortText":"000035bb"}
> > +# CHECK-DAG:
> > {"detail":"int","filterText":"ccc","insertText":"ccc","inser
> tTextFormat":1,"ki
> > nd":5,"label":"ccc","sortText":"000035ccc"}
> > +# CHECK-DAG: {"detail":"fake
> > &","filterText":"operator=","insertText":"operator=(${1:const fake
> > &})","insertTextFormat":2,"kind":2,"label":"operator=(const fake
> > &)","sortText":"000079operator="}
> > +# CHECK-DAG:
> > {"detail":"void","filterText":"~fake","insertText":"~fake()"
> ,"insertTextFormat
> > ":1,"kind":4,"label":"~fake()","sortText":"000079~fake"}
> > +# CHECK-DAG: {"detail":"int","filterText":"f","insertText":"f(${1:int
> i},
> > ${2:const float f})","insertTextFormat":2,"kind":2,"label":"f(int i,
> const
> > float f) const","sortText":"000035f"}
> > +# CHECK-SAME: ]}
> >  # Update the source file and check for completions again.
> >  Content-Length: 226
> >
> > @@ -44,15 +44,12 @@ Content-Length: 226
> >  Content-Length: 148
> >
> >
> > {"jsonrpc":"2.0","id":3,"method":"textDocument/completion","
> params":{"textDocu
> > ment":{"uri":"file:///main.cpp"},"position":{"line":3,"character":5}}}
> > -# Repeat the completion request, expect the same results.
> > -#
> > -# CHECK: {"jsonrpc":"2.0","id":3,"result":[
> > -# CHECK-DAG: {"label":"func()","kind":2,"detail":"int (*)(int,
> > int)","sortText":"000034func","filterText":"func","insertTex
> t":"func()","inser
> > tTextFormat":1}
> > -# CHECK: ]}
> > +# CHECK: {"id":3,"jsonrpc":"2.0","result":[
> > +# CHECK-DAG: {"detail":"int (*)(int,
> > int)","filterText":"func","insertText":"func()","insertTextF
> ormat":1,"kind":2,
> > "label":"func()","sortText":"000034func"}
> > +# CHECK-SAME: ]}
> >  Content-Length: 44
> >
> >  {"jsonrpc":"2.0","id":4,"method":"shutdown"}
> > -# CHECK: {"jsonrpc":"2.0","id":4,"result":null}
> >  Content-Length: 33
> >
> >  {"jsonrpc":"2.0":"method":"exit"}
> >
> > Modified: clang-tools-extra/trunk/test/clangd/completion.test
> > URL: http://llvm.org/viewvc/llvm-project/clang-tools-
> > extra/trunk/test/clangd/completion.test?rev=317486&r1=317485
> &r2=317486&view=di
> > ff
> > ============================================================
> ==================
> > --- clang-tools-extra/trunk/test/clangd/completion.test (original)
> > +++ clang-tools-extra/trunk/test/clangd/completion.test Mon Nov  6
> 07:40:30
> > 2017
> > @@ -15,27 +15,27 @@ Content-Length: 148
> >  # The order of results returned by codeComplete seems to be
> >  # nondeterministic, so we check regardless of order.
> >  #
> > -# CHECK: {"jsonrpc":"2.0","id":1,"result":[
> > -# CHECK-DAG:
> > {"label":"a","kind":5,"detail":"int","sortText":"000035a","f
> ilterText":"a","in
> > sertText":"a","insertTextFormat":1}
> > -# CHECK-DAG:
> > {"label":"bb","kind":5,"detail":"int","sortText":"000035bb",
> "filterText":"bb",
> > "insertText":"bb","insertTextFormat":1}
> > -# CHECK-DAG:
> > {"label":"ccc","kind":5,"detail":"int","sortText":"000035ccc
> ","filterText":"cc
> > c","insertText":"ccc","insertTextFormat":1}
> > -# CHECK-DAG: {"label":"operator=(const fake &)","kind":2,"detail":"fake
> > &","sortText":"000079operator=","filterText":"operator=","in
> sertText":"operato
> > r=","insertTextFormat":1}
> > -# CHECK-DAG:
> > {"label":"~fake()","kind":4,"detail":"void","sortText":"0000
> 79~fake","filterTe
> > xt":"~fake","insertText":"~fake","insertTextFormat":1}
> > -# CHECK-DAG: {"label":"f(int i, const float f)
> > const","kind":2,"detail":"int","sortText":"000035f","filterT
> ext":"f","insertTe
> > xt":"f","insertTextFormat":1}
> > -# CHECK: ]}
> > +# CHECK: {"id":1,"jsonrpc":"2.0","result":[
> > +# CHECK-DAG:
> > {"detail":"int","filterText":"a","insertText":"a","insertTex
> tFormat":1,"kind":
> > 5,"label":"a","sortText":"000035a"}
> > +# CHECK-DAG:
> > {"detail":"int","filterText":"bb","insertText":"bb","insertT
> extFormat":1,"kind
> > ":5,"label":"bb","sortText":"000035bb"}
> > +# CHECK-DAG:
> > {"detail":"int","filterText":"ccc","insertText":"ccc","inser
> tTextFormat":1,"ki
> > nd":5,"label":"ccc","sortText":"000035ccc"}
> > +# CHECK-DAG: {"detail":"fake
> > &","filterText":"operator=","insertText":"operator=","insert
> TextFormat":1,"kin
> > d":2,"label":"operator=(const fake &)","sortText":"000079operator="}
> > +# CHECK-DAG:
> > {"detail":"void","filterText":"~fake","insertText":"~fake","
> insertTextFormat":
> > 1,"kind":4,"label":"~fake()","sortText":"000079~fake"}
> > +# CHECK-DAG:
> > {"detail":"int","filterText":"f","insertText":"f","insertTex
> tFormat":1,"kind":
> > 2,"label":"f(int i, const float f) const","sortText":"000035f"}
> > +# CHECK-SAME: ]}
> >  Content-Length: 148
> >
> >
> > {"jsonrpc":"2.0","id":2,"method":"textDocument/completion","
> params":{"textDocu
> > ment":{"uri":"file:///main.cpp"},"position":{"line":3,"character":5}}}
> >  # Repeat the completion request, expect the same results.
> >  #
> > -# CHECK: {"jsonrpc":"2.0","id":2,"result":[
> > -# CHECK-DAG:
> > {"label":"a","kind":5,"detail":"int","sortText":"000035a","f
> ilterText":"a","in
> > sertText":"a","insertTextFormat":1}
> > -# CHECK-DAG:
> > {"label":"bb","kind":5,"detail":"int","sortText":"000035bb",
> "filterText":"bb",
> > "insertText":"bb","insertTextFormat":1}
> > -# CHECK-DAG:
> > {"label":"ccc","kind":5,"detail":"int","sortText":"000035ccc
> ","filterText":"cc
> > c","insertText":"ccc","insertTextFormat":1}
> > -# CHECK-DAG: {"label":"operator=(const fake &)","kind":2,"detail":"fake
> > &","sortText":"000079operator=","filterText":"operator=","in
> sertText":"operato
> > r=","insertTextFormat":1}
> > -# CHECK-DAG:
> > {"label":"~fake()","kind":4,"detail":"void","sortText":"0000
> 79~fake","filterTe
> > xt":"~fake","insertText":"~fake","insertTextFormat":1}
> > -# CHECK-DAG: {"label":"f(int i, const float f)
> > const","kind":2,"detail":"int","sortText":"000035f","filterT
> ext":"f","insertTe
> > xt":"f","insertTextFormat":1}
> > -# CHECK: ]}
> > +# CHECK: {"id":2,"jsonrpc":"2.0","result":[
> > +# CHECK-DAG:
> > {"detail":"int","filterText":"a","insertText":"a","insertTex
> tFormat":1,"kind":
> > 5,"label":"a","sortText":"000035a"}
> > +# CHECK-DAG:
> > {"detail":"int","filterText":"bb","insertText":"bb","insertT
> extFormat":1,"kind
> > ":5,"label":"bb","sortText":"000035bb"}
> > +# CHECK-DAG:
> > {"detail":"int","filterText":"ccc","insertText":"ccc","inser
> tTextFormat":1,"ki
> > nd":5,"label":"ccc","sortText":"000035ccc"}
> > +# CHECK-DAG: {"detail":"fake
> > &","filterText":"operator=","insertText":"operator=","insert
> TextFormat":1,"kin
> > d":2,"label":"operator=(const fake &)","sortText":"000079operator="}
> > +# CHECK-DAG:
> > {"detail":"void","filterText":"~fake","insertText":"~fake","
> insertTextFormat":
> > 1,"kind":4,"label":"~fake()","sortText":"000079~fake"}
> > +# CHECK-DAG:
> > {"detail":"int","filterText":"f","insertText":"f","insertTex
> tFormat":1,"kind":
> > 2,"label":"f(int i, const float f) const","sortText":"000035f"}
> > +# CHECK-SAME: ]}
> >  # Update the source file and check for completions again.
> >  Content-Length: 226
> >
> > @@ -46,13 +46,12 @@ Content-Length: 148
> >
> > {"jsonrpc":"2.0","id":3,"method":"textDocument/completion","
> params":{"textDocu
> > ment":{"uri":"file:///main.cpp"},"position":{"line":3,"character":5}}}
> >  # Repeat the completion request, expect the same results.
> >  #
> > -# CHECK: {"jsonrpc":"2.0","id":3,"result":[
> > -# CHECK-DAG: {"label":"func()","kind":2,"detail":"int (*)(int,
> > int)","sortText":"000034func","filterText":"func","insertTex
> t":"func","insertT
> > extFormat":1}
> > -# CHECK: ]}
> > +# CHECK: {"id":3,"jsonrpc":"2.0","result":[
> > +# CHECK-DAG: {"detail":"int (*)(int,
> > int)","filterText":"func","insertText":"func","insertTextFor
> mat":1,"kind":2,"l
> > abel":"func()","sortText":"000034func"}
> > +# CHECK-SAME: ]}
> >  Content-Length: 44
> >
> >  {"jsonrpc":"2.0","id":4,"method":"shutdown"}
> > -# CHECK: {"jsonrpc":"2.0","id":4,"result":null}
> >  Content-Length: 33
> >
> >  {"jsonrpc":"2.0":"method":"exit"}
> >
> > Modified: clang-tools-extra/trunk/test/clangd/definitions.test
> > URL: http://llvm.org/viewvc/llvm-project/clang-tools-
> > extra/trunk/test/clangd/definitions.test?rev=317486&r1=31748
> 5&r2=317486&view=d
> > iff
> > ============================================================
> ==================
> > --- clang-tools-extra/trunk/test/clangd/definitions.test (original)
> > +++ clang-tools-extra/trunk/test/clangd/definitions.test Mon Nov  6
> 07:40:30
> > 2017
> > @@ -1,4 +1,4 @@
> > -# RUN: clangd -run-synchronously < %s | FileCheck %s
> > +# RUN: clangd -pretty -run-synchronously < %s | FileCheck
> -strict-whitespace
> > %s
> >  # It is absolutely vital that this file has CRLF line endings.
> >  #
> >  Content-Length: 125
> > @@ -13,14 +13,44 @@ Content-Length: 148
> >
> >
> > {"jsonrpc":"2.0","id":1,"method":"textDocument/definition","
> params":{"textDocu
> > ment":{"uri":"file:///main.cpp"},"position":{"line":2,"character":0}}}
> >  # Go to local variable
> > -# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp",
> > "range": {"start": {"line": 1, "character": 0}, "end": {"line": 1,
> > "character": 5}}}]}
> > -
> > +#      CHECK:  "id": 1,
> > +# CHECK-NEXT:  "jsonrpc": "2.0",
> > +# CHECK-NEXT:  "result": [
> > +# CHECK-NEXT:    {
> > +# CHECK-NEXT:      "range": {
> > +# CHECK-NEXT:        "end": {
> > +# CHECK-NEXT:          "character": 5,
> > +# CHECK-NEXT:          "line": 1
> > +# CHECK-NEXT:        },
> > +# CHECK-NEXT:        "start": {
> > +# CHECK-NEXT:          "character": 0,
> > +# CHECK-NEXT:          "line": 1
> > +# CHECK-NEXT:        }
> > +# CHECK-NEXT:      },
> > +# CHECK-NEXT:      "uri": "file:///main.cpp"
> > +# CHECK-NEXT:    }
> > +# CHECK-NEXT:  ]
> >  Content-Length: 148
> >
> >
> > {"jsonrpc":"2.0","id":1,"method":"textDocument/definition","
> params":{"textDocu
> > ment":{"uri":"file:///main.cpp"},"position":{"line":2,"character":1}}}
> >  # Go to local variable, end of token
> > -# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp",
> > "range": {"start": {"line": 1, "character": 0}, "end": {"line": 1,
> > "character": 5}}}]}
> > -
> > +#      CHECK:  "id": 1,
> > +# CHECK-NEXT:  "jsonrpc": "2.0",
> > +# CHECK-NEXT:  "result": [
> > +# CHECK-NEXT:    {
> > +# CHECK-NEXT:      "range": {
> > +# CHECK-NEXT:        "end": {
> > +# CHECK-NEXT:          "character": 5,
> > +# CHECK-NEXT:          "line": 1
> > +# CHECK-NEXT:        },
> > +# CHECK-NEXT:        "start": {
> > +# CHECK-NEXT:          "character": 0,
> > +# CHECK-NEXT:          "line": 1
> > +# CHECK-NEXT:        }
> > +# CHECK-NEXT:      },
> > +# CHECK-NEXT:      "uri": "file:///main.cpp"
> > +# CHECK-NEXT:    }
> > +# CHECK-NEXT:  ]
> >  Content-Length: 214
> >
> >
> > {"jsonrpc":"2.0","method":"textDocument/didChange","params":
> {"textDocument":{"
> > uri":"file:///main.cpp","version":2},"contentChanges":[{"text":"struct
> Foo
> > {\nint x;\n};\nint main() {\n  Foo bar = { x : 1 };\n}\n"}]}}
> > @@ -29,8 +59,23 @@ Content-Length: 149
> >
> >
> > {"jsonrpc":"2.0","id":1,"method":"textDocument/definition","
> params":{"textDocu
> > ment":{"uri":"file:///main.cpp"},"position":{"line":4,"character":14}}}
> >  # Go to field, GNU old-style field designator
> > -# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp",
> > "range": {"start": {"line": 1, "character": 0}, "end": {"line": 1,
> > "character": 5}}}]}
> > -
> > +#      CHECK:  "id": 1,
> > +# CHECK-NEXT:  "jsonrpc": "2.0",
> > +# CHECK-NEXT:  "result": [
> > +# CHECK-NEXT:    {
> > +# CHECK-NEXT:      "range": {
> > +# CHECK-NEXT:        "end": {
> > +# CHECK-NEXT:          "character": 5,
> > +# CHECK-NEXT:          "line": 1
> > +# CHECK-NEXT:        },
> > +# CHECK-NEXT:        "start": {
> > +# CHECK-NEXT:          "character": 0,
> > +# CHECK-NEXT:          "line": 1
> > +# CHECK-NEXT:        }
> > +# CHECK-NEXT:      },
> > +# CHECK-NEXT:      "uri": "file:///main.cpp"
> > +# CHECK-NEXT:    }
> > +# CHECK-NEXT:  ]
> >  Content-Length: 215
> >
> >
> > {"jsonrpc":"2.0","method":"textDocument/didChange","params":
> {"textDocument":{"
> > uri":"file:///main.cpp","version":3},"contentChanges":[{"text":"struct
> Foo
> > {\nint x;\n};\nint main() {\n  Foo baz = { .x = 2 };\n}\n"}]}}
> > @@ -39,8 +84,23 @@ Content-Length: 149
> >
> >
> > {"jsonrpc":"2.0","id":1,"method":"textDocument/definition","
> params":{"textDocu
> > ment":{"uri":"file:///main.cpp"},"position":{"line":4,"character":15}}}
> >  # Go to field, field designator
> > -# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp",
> > "range": {"start": {"line": 1, "character": 0}, "end": {"line": 1,
> > "character": 5}}}]}
> > -
> > +#      CHECK:  "id": 1,
> > +# CHECK-NEXT:  "jsonrpc": "2.0",
> > +# CHECK-NEXT:  "result": [
> > +# CHECK-NEXT:    {
> > +# CHECK-NEXT:      "range": {
> > +# CHECK-NEXT:        "end": {
> > +# CHECK-NEXT:          "character": 5,
> > +# CHECK-NEXT:          "line": 1
> > +# CHECK-NEXT:        },
> > +# CHECK-NEXT:        "start": {
> > +# CHECK-NEXT:          "character": 0,
> > +# CHECK-NEXT:          "line": 1
> > +# CHECK-NEXT:        }
> > +# CHECK-NEXT:      },
> > +# CHECK-NEXT:      "uri": "file:///main.cpp"
> > +# CHECK-NEXT:    }
> > +# CHECK-NEXT:  ]
> >  Content-Length: 187
> >
> >
> > {"jsonrpc":"2.0","method":"textDocument/didChange","params":
> {"textDocument":{"
> > uri":"file:///main.cpp","version":4},"contentChanges":[{"text":"int
> main() {\n
> > main();\n   return 0;\n}"}]}}
> > @@ -49,8 +109,23 @@ Content-Length: 148
> >
> >
> > {"jsonrpc":"2.0","id":1,"method":"textDocument/definition","
> params":{"textDocu
> > ment":{"uri":"file:///main.cpp"},"position":{"line":1,"character":3}}}
> >  # Go to function declaration, function call
> > -# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp",
> > "range": {"start": {"line": 0, "character": 0}, "end": {"line": 3,
> > "character": 1}}}]}
> > -
> > +#      CHECK:  "id": 1,
> > +# CHECK-NEXT:  "jsonrpc": "2.0",
> > +# CHECK-NEXT:  "result": [
> > +# CHECK-NEXT:    {
> > +# CHECK-NEXT:      "range": {
> > +# CHECK-NEXT:        "end": {
> > +# CHECK-NEXT:          "character": 1,
> > +# CHECK-NEXT:          "line": 3
> > +# CHECK-NEXT:        },
> > +# CHECK-NEXT:        "start": {
> > +# CHECK-NEXT:          "character": 0,
> > +# CHECK-NEXT:          "line": 0
> > +# CHECK-NEXT:        }
> > +# CHECK-NEXT:      },
> > +# CHECK-NEXT:      "uri": "file:///main.cpp"
> > +# CHECK-NEXT:    }
> > +# CHECK-NEXT:  ]
> >  Content-Length: 208
> >
> >
> > {"jsonrpc":"2.0","method":"textDocument/didChange","params":
> {"textDocument":{"
> > uri":"file:///main.cpp","version":5},"contentChanges":[{"text":"struct
> Foo
> > {\n};\nint main() {\n   Foo bar;\n   return 0;\n}\n"}]}}
> > @@ -59,8 +134,23 @@ Content-Length: 148
> >
> >
> > {"jsonrpc":"2.0","id":1,"method":"textDocument/definition","
> params":{"textDocu
> > ment":{"uri":"file:///main.cpp"},"position":{"line":3,"character":3}}}
> >  # Go to struct declaration, new struct instance
> > -# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp",
> > "range": {"start": {"line": 0, "character": 0}, "end": {"line": 1,
> > "character": 1}}}]}
> > -
> > +#      CHECK:  "id": 1,
> > +# CHECK-NEXT:  "jsonrpc": "2.0",
> > +# CHECK-NEXT:  "result": [
> > +# CHECK-NEXT:    {
> > +# CHECK-NEXT:      "range": {
> > +# CHECK-NEXT:        "end": {
> > +# CHECK-NEXT:          "character": 1,
> > +# CHECK-NEXT:          "line": 1
> > +# CHECK-NEXT:        },
> > +# CHECK-NEXT:        "start": {
> > +# CHECK-NEXT:          "character": 0,
> > +# CHECK-NEXT:          "line": 0
> > +# CHECK-NEXT:        }
> > +# CHECK-NEXT:      },
> > +# CHECK-NEXT:      "uri": "file:///main.cpp"
> > +# CHECK-NEXT:    }
> > +# CHECK-NEXT:  ]
> >  Content-Length: 231
> >
> >
> > {"jsonrpc":"2.0","method":"textDocument/didChange","params":
> {"textDocument":{"
> > uri":"file:///main.cpp","version":5},"contentChanges":[{"text":"namespace
> n1
> > {\nstruct Foo {\n};\n}\nint main() {\n   n1::Foo bar;\n   return
> 0;\n}\n"}]}}
> > @@ -69,8 +159,23 @@ Content-Length: 148
> >
> >
> > {"jsonrpc":"2.0","id":1,"method":"textDocument/definition","
> params":{"textDocu
> > ment":{"uri":"file:///main.cpp"},"position":{"line":5,"character":4}}}
> >  # Go to struct declaration, new struct instance, qualified name
> > -# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp",
> > "range": {"start": {"line": 0, "character": 0}, "end": {"line": 3,
> > "character": 1}}}]}
> > -
> > +#      CHECK:  "id": 1,
> > +# CHECK-NEXT:  "jsonrpc": "2.0",
> > +# CHECK-NEXT:  "result": [
> > +# CHECK-NEXT:    {
> > +# CHECK-NEXT:      "range": {
> > +# CHECK-NEXT:        "end": {
> > +# CHECK-NEXT:          "character": 1,
> > +# CHECK-NEXT:          "line": 3
> > +# CHECK-NEXT:        },
> > +# CHECK-NEXT:        "start": {
> > +# CHECK-NEXT:          "character": 0,
> > +# CHECK-NEXT:          "line": 0
> > +# CHECK-NEXT:        }
> > +# CHECK-NEXT:      },
> > +# CHECK-NEXT:      "uri": "file:///main.cpp"
> > +# CHECK-NEXT:    }
> > +# CHECK-NEXT:  ]
> >  Content-Length: 215
> >
> >
> > {"jsonrpc":"2.0","method":"textDocument/didChange","params":
> {"textDocument":{"
> > uri":"file:///main.cpp","version":6},"contentChanges":[{"text":"struct
> Foo {\n
> > int x;\n};\nint main() {\n   Foo bar;\n   bar.x;\n}\n"}]}}
> > @@ -79,8 +184,23 @@ Content-Length: 148
> >
> >
> > {"jsonrpc":"2.0","id":1,"method":"textDocument/definition","
> params":{"textDocu
> > ment":{"uri":"file:///main.cpp"},"position":{"line":5,"character":7}}}
> >  # Go to field declaration, field reference
> > -# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp",
> > "range": {"start": {"line": 1, "character": 2}, "end": {"line": 1,
> > "character": 7}}}]}
> > -
> > +#      CHECK:  "id": 1,
> > +# CHECK-NEXT:  "jsonrpc": "2.0",
> > +# CHECK-NEXT:  "result": [
> > +# CHECK-NEXT:    {
> > +# CHECK-NEXT:      "range": {
> > +# CHECK-NEXT:        "end": {
> > +# CHECK-NEXT:          "character": 7,
> > +# CHECK-NEXT:          "line": 1
> > +# CHECK-NEXT:        },
> > +# CHECK-NEXT:        "start": {
> > +# CHECK-NEXT:          "character": 2,
> > +# CHECK-NEXT:          "line": 1
> > +# CHECK-NEXT:        }
> > +# CHECK-NEXT:      },
> > +# CHECK-NEXT:      "uri": "file:///main.cpp"
> > +# CHECK-NEXT:    }
> > +# CHECK-NEXT:  ]
> >  Content-Length: 220
> >
> >
> > {"jsonrpc":"2.0","method":"textDocument/didChange","params":
> {"textDocument":{"
> > uri":"file:///main.cpp","version":7},"contentChanges":[{"text":"struct
> Foo {\n
> > void x();\n};\nint main() {\n   Foo bar;\n   bar.x();\n}\n"}]}}
> > @@ -89,8 +209,23 @@ Content-Length: 148
> >
> >
> > {"jsonrpc":"2.0","id":1,"method":"textDocument/definition","
> params":{"textDocu
> > ment":{"uri":"file:///main.cpp"},"position":{"line":5,"character":7}}}
> >  # Go to method declaration, method call
> > -# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp",
> > "range": {"start": {"line": 1, "character": 2}, "end": {"line": 1,
> > "character": 10}}}]}
> > -
> > +#      CHECK:  "id": 1,
> > +# CHECK-NEXT:  "jsonrpc": "2.0",
> > +# CHECK-NEXT:  "result": [
> > +# CHECK-NEXT:    {
> > +# CHECK-NEXT:      "range": {
> > +# CHECK-NEXT:        "end": {
> > +# CHECK-NEXT:          "character": 10,
> > +# CHECK-NEXT:          "line": 1
> > +# CHECK-NEXT:        },
> > +# CHECK-NEXT:        "start": {
> > +# CHECK-NEXT:          "character": 2,
> > +# CHECK-NEXT:          "line": 1
> > +# CHECK-NEXT:        }
> > +# CHECK-NEXT:      },
> > +# CHECK-NEXT:      "uri": "file:///main.cpp"
> > +# CHECK-NEXT:    }
> > +# CHECK-NEXT:  ]
> >  Content-Length: 240
> >
> >
> > {"jsonrpc":"2.0","method":"textDocument/didChange","params":
> {"textDocument":{"
> > uri":"file:///main.cpp","version":7},"contentChanges":[{"text":"struct
> Foo
> > {\n};\ntypedef Foo TypedefFoo;\nint main() {\n   TypedefFoo bar;\n
>  return
> > 0;\n}\n"}]}}
> > @@ -99,8 +234,23 @@ Content-Length: 149
> >
> >
> > {"jsonrpc":"2.0","id":1,"method":"textDocument/definition","
> params":{"textDocu
> > ment":{"uri":"file:///main.cpp"},"position":{"line":4,"character":10}}}
> >  # Go to typedef
> > -# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp",
> > "range": {"start": {"line": 2, "character": 0}, "end": {"line": 2,
> > "character": 22}}}]}
> > -
> > +#      CHECK:  "id": 1,
> > +# CHECK-NEXT:  "jsonrpc": "2.0",
> > +# CHECK-NEXT:  "result": [
> > +# CHECK-NEXT:    {
> > +# CHECK-NEXT:      "range": {
> > +# CHECK-NEXT:        "end": {
> > +# CHECK-NEXT:          "character": 22,
> > +# CHECK-NEXT:          "line": 2
> > +# CHECK-NEXT:        },
> > +# CHECK-NEXT:        "start": {
> > +# CHECK-NEXT:          "character": 0,
> > +# CHECK-NEXT:          "line": 2
> > +# CHECK-NEXT:        }
> > +# CHECK-NEXT:      },
> > +# CHECK-NEXT:      "uri": "file:///main.cpp"
> > +# CHECK-NEXT:    }
> > +# CHECK-NEXT:  ]
> >  Content-Length: 254
> >
> >
> > {"jsonrpc":"2.0","method":"textDocument/didChange","params":
> {"textDocument":{"
> > uri":"file:///main.cpp","version":7},"contentChanges":[{"text":"template
> > <typename MyTemplateParam>\nvoid foo() {\n MyTemplateParam a;\n}\nint
> main()
> > {\n   return 0;\n}\n"}]}}
> > @@ -109,8 +259,9 @@ Content-Length: 149
> >
> >
> > {"jsonrpc":"2.0","id":1,"method":"textDocument/definition","
> params":{"textDocu
> > ment":{"uri":"file:///main.cpp"},"position":{"line":2,"character":13}}}
> >  # Go to template type parameter. Fails until clangIndex is modified to
> handle
> > those.
> > -# no-CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri":
> "file:///main.cpp",
> > "range": {"start": {"line": 0, "character": 10}, "end": {"line": 0,
> > "character": 34}}}]}
> > -
> > +#      CHECK:  "id": 1,
> > +# CHECK-NEXT:  "jsonrpc": "2.0",
> > +# CHECK-NEXT:  "result": []
> >  Content-Length: 256
> >
> >
> > {"jsonrpc":"2.0","method":"textDocument/didChange","params":
> {"textDocument":{"
> > uri":"file:///main.cpp","version":7},"contentChanges":[{"text":"namespace
> ns
> > {\nstruct Foo {\nstatic void bar() {}\n};\n}\nint main() {\n
> > ns::Foo::bar();\n   return 0;\n}\n"}]}}
> > @@ -119,8 +270,23 @@ Content-Length: 148
> >
> >
> > {"jsonrpc":"2.0","id":1,"method":"textDocument/definition","
> params":{"textDocu
> > ment":{"uri":"file:///main.cpp"},"position":{"line":6,"character":4}}}
> >  # Go to namespace, static method call
> > -# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp",
> > "range": {"start": {"line": 0, "character": 0}, "end": {"line": 4,
> > "character": 1}}}]}
> > -
> > +#      CHECK:  "id": 1,
> > +# CHECK-NEXT:  "jsonrpc": "2.0",
> > +# CHECK-NEXT:  "result": [
> > +# CHECK-NEXT:    {
> > +# CHECK-NEXT:      "range": {
> > +# CHECK-NEXT:        "end": {
> > +# CHECK-NEXT:          "character": 1,
> > +# CHECK-NEXT:          "line": 4
> > +# CHECK-NEXT:        },
> > +# CHECK-NEXT:        "start": {
> > +# CHECK-NEXT:          "character": 0,
> > +# CHECK-NEXT:          "line": 0
> > +# CHECK-NEXT:        }
> > +# CHECK-NEXT:      },
> > +# CHECK-NEXT:      "uri": "file:///main.cpp"
> > +# CHECK-NEXT:    }
> > +# CHECK-NEXT:  ]
> >  Content-Length: 265
> >
> >
> > {"jsonrpc":"2.0","method":"textDocument/didChange","params":
> {"textDocument":{"
> > uri":"file:///main.cpp","version":7},"contentChanges":[{"text":"namespace
> ns
> > {\nstruct Foo {\n  int field;\n  Foo(int param) : field(param)
> {}\n};\n}\nint
> > main() {\n   return 0;\n}\n"}]}}
> > @@ -128,9 +294,24 @@ Content-Length: 265
> >  Content-Length: 149
> >
> >
> > {"jsonrpc":"2.0","id":1,"method":"textDocument/definition","
> params":{"textDocu
> > ment":{"uri":"file:///main.cpp"},"position":{"line":3,"character":21}}}
> > -# Go to field, member initializer
> > -# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp",
> > "range": {"start": {"line": 2, "character": 2}, "end": {"line": 2,
> > "character": 11}}}]}
> > -
> > +# Go to field, member initializer
> > +#      CHECK:  "id": 1,
> > +# CHECK-NEXT:  "jsonrpc": "2.0",
> > +# CHECK-NEXT:  "result": [
> > +# CHECK-NEXT:    {
> > +# CHECK-NEXT:      "range": {
> > +# CHECK-NEXT:        "end": {
> > +# CHECK-NEXT:          "character": 11,
> > +# CHECK-NEXT:          "line": 2
> > +# CHECK-NEXT:        },
> > +# CHECK-NEXT:        "start": {
> > +# CHECK-NEXT:          "character": 2,
> > +# CHECK-NEXT:          "line": 2
> > +# CHECK-NEXT:        }
> > +# CHECK-NEXT:      },
> > +# CHECK-NEXT:      "uri": "file:///main.cpp"
> > +# CHECK-NEXT:    }
> > +# CHECK-NEXT:  ]
> >  Content-Length: 204
> >
> >
> > {"jsonrpc":"2.0","method":"textDocument/didChange","params":
> {"textDocument":{"
> > uri":"file:///main.cpp","version":7},"contentChanges":[{"text":"#define
> > MY_MACRO 0\nint main() {\n  return MY_MACRO;\n}\n"}]}}
> > @@ -139,8 +320,23 @@ Content-Length: 148
> >
> >
> > {"jsonrpc":"2.0","id":1,"method":"textDocument/definition","
> params":{"textDocu
> > ment":{"uri":"file:///main.cpp"},"position":{"line":2,"character":9}}}
> >  # Go to macro.
> > -# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///{{([A-Za-
> > z]:/)?}}main.cpp", "range": {"start": {"line": 0, "character": 8}, "end":
> > {"line": 0, "character": 18}}}]}
> > -
> > +#      CHECK:  "id": 1,
> > +# CHECK-NEXT:  "jsonrpc": "2.0",
> > +# CHECK-NEXT:  "result": [
> > +# CHECK-NEXT:    {
> > +# CHECK-NEXT:      "range": {
> > +# CHECK-NEXT:        "end": {
> > +# CHECK-NEXT:          "character": 18,
> > +# CHECK-NEXT:          "line": 0
> > +# CHECK-NEXT:        },
> > +# CHECK-NEXT:        "start": {
> > +# CHECK-NEXT:          "character": 8,
> > +# CHECK-NEXT:          "line": 0
> > +# CHECK-NEXT:        }
> > +# CHECK-NEXT:      },
> > +# CHECK-NEXT:      "uri": "file:///main.cpp"
> > +# CHECK-NEXT:    }
> > +# CHECK-NEXT:  ]
> >  Content-Length: 217
> >
> >
> > {"jsonrpc":"2.0","method":"textDocument/didChange","params":
> {"textDocument":{"
> > uri":"file:///main.cpp","version":7},"contentChanges":[{"text":"#define
> FOO
> > 1\nint a = FOO;\n#define FOO 2\nint b = FOO;\n#undef FOO\n"}]}}
> > @@ -149,29 +345,77 @@ Content-Length: 148
> >
> >
> > {"jsonrpc":"2.0","id":1,"method":"textDocument/definition","
> params":{"textDocu
> > ment":{"uri":"file:///main.cpp"},"position":{"line":1,"character":8}}}
> >  # Go to macro, re-defined later
> > -# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///{{([A-Za-
> > z]:/)?}}main.cpp", "range": {"start": {"line": 0, "character": 8}, "end":
> > {"line": 0, "character": 13}}}]}
> > -
> > +#      CHECK:  "id": 1,
> > +# CHECK-NEXT:  "jsonrpc": "2.0",
> > +# CHECK-NEXT:  "result": [
> > +# CHECK-NEXT:    {
> > +# CHECK-NEXT:      "range": {
> > +# CHECK-NEXT:        "end": {
> > +# CHECK-NEXT:          "character": 13,
> > +# CHECK-NEXT:          "line": 0
> > +# CHECK-NEXT:        },
> > +# CHECK-NEXT:        "start": {
> > +# CHECK-NEXT:          "character": 8,
> > +# CHECK-NEXT:          "line": 0
> > +# CHECK-NEXT:        }
> > +# CHECK-NEXT:      },
> > +# CHECK-NEXT:      "uri": "file:///main.cpp"
> > +# CHECK-NEXT:    }
> > +# CHECK-NEXT:  ]
> >  Content-Length: 148
> >
> >
> > {"jsonrpc":"2.0","id":1,"method":"textDocument/definition","
> params":{"textDocu
> > ment":{"uri":"file:///main.cpp"},"position":{"line":3,"character":8}}}
> >  # Go to macro, undefined later
> > -# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp",
> > "range": {"start": {"line": 2, "character": 8}, "end": {"line": 2,
> > "character": 13}}}]}
> > -
> > +#      CHECK:  "id": 1,
> > +# CHECK-NEXT:  "jsonrpc": "2.0",
> > +# CHECK-NEXT:  "result": [
> > +# CHECK-NEXT:    {
> > +# CHECK-NEXT:      "range": {
> > +# CHECK-NEXT:        "end": {
> > +# CHECK-NEXT:          "character": 13,
> > +# CHECK-NEXT:          "line": 2
> > +# CHECK-NEXT:        },
> > +# CHECK-NEXT:        "start": {
> > +# CHECK-NEXT:          "character": 8,
> > +# CHECK-NEXT:          "line": 2
> > +# CHECK-NEXT:        }
> > +# CHECK-NEXT:      },
> > +# CHECK-NEXT:      "uri": "file:///main.cpp"
> > +# CHECK-NEXT:    }
> > +# CHECK-NEXT:  ]
> >  Content-Length: 148
> >
> >
> > {"jsonrpc":"2.0","id":1,"method":"textDocument/definition","
> params":{"textDocu
> > ment":{"uri":"file:///main.cpp"},"position":{"line":4,"character":7}}}
> >  # Go to macro, being undefined
> > -# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp",
> > "range": {"start": {"line": 2, "character": 8}, "end": {"line": 2,
> > "character": 13}}}]}
> > -
> > +#      CHECK:  "id": 1,
> > +# CHECK-NEXT:  "jsonrpc": "2.0",
> > +# CHECK-NEXT:  "result": [
> > +# CHECK-NEXT:    {
> > +# CHECK-NEXT:      "range": {
> > +# CHECK-NEXT:        "end": {
> > +# CHECK-NEXT:          "character": 13,
> > +# CHECK-NEXT:          "line": 2
> > +# CHECK-NEXT:        },
> > +# CHECK-NEXT:        "start": {
> > +# CHECK-NEXT:          "character": 8,
> > +# CHECK-NEXT:          "line": 2
> > +# CHECK-NEXT:        }
> > +# CHECK-NEXT:      },
> > +# CHECK-NEXT:      "uri": "file:///main.cpp"
> > +# CHECK-NEXT:    }
> > +# CHECK-NEXT:  ]
> >  Content-Length: 156
> >
> >
> > {"jsonrpc":"2.0","id":2,"method":"textDocument/definition","
> params":{"textDocu
> > ment":{"uri":"file:///doesnotexist.cpp"},"position":{"line":
> 4,"character":7}}}
> > -# CHECK: {"jsonrpc":"2.0","id":2,"error":{"code":-
> > 32602,"message":"findDefinitions called on non-added file"}}
> > -
> > +#      CHECK:  "error": {
> > +# CHECK-NEXT:    "code": -32602,
> > +# CHECK-NEXT:    "message": "findDefinitions called on non-added file"
> > +# CHECK-NEXT:  },
> > +# CHECK-NEXT:  "id": 2,
> > +# CHECK-NEXT:  "jsonrpc": "2.0"
> >  Content-Length: 48
> >
> >  {"jsonrpc":"2.0","id":10000,"method":"shutdown"}
> > -# CHECK: {"jsonrpc":"2.0","id":10000,"result":null}
> >  Content-Length: 33
> >
> >  {"jsonrpc":"2.0":"method":"exit"}
> >
> > Modified: clang-tools-extra/trunk/test/clangd/diagnostics-preamble.test
> > URL: http://llvm.org/viewvc/llvm-project/clang-tools-
> > extra/trunk/test/clangd/diagnostics-
> > preamble.test?rev=317486&r1=317485&r2=317486&view=diff
> > ============================================================
> ==================
> > --- clang-tools-extra/trunk/test/clangd/diagnostics-preamble.test
> (original)
> > +++ clang-tools-extra/trunk/test/clangd/diagnostics-preamble.test Mon
> Nov  6
> > 07:40:30 2017
> > @@ -1,4 +1,4 @@
> > -# RUN: clangd -run-synchronously < %s | FileCheck %s
> > +# RUN: clangd -pretty -run-synchronously < %s | FileCheck
> -strict-whitespace
> > %s
> >  # It is absolutely vital that this file has CRLF line endings.
> >  #
> >  Content-Length: 125
> > @@ -8,12 +8,14 @@ Content-Length: 125
> >  Content-Length: 206
> >
> >
> > {"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"
> textDocument":{"ur
> > i":"file:///main.cpp","languageId":"cpp","version":1,"text":"#ifndef
> > FOO\n#define FOO\nint a;\n#else\nint a = b;#endif\n\n\n"}}}
> > -# CHECK:
> > {"jsonrpc":"2.0","method":"textDocument/publishDiagnostics",
> "params":{"uri":"f
> > ile:///main.cpp","diagnostics":[]}}
> > -
> > +#      CHECK:  "method": "textDocument/publishDiagnostics",
> > +# CHECK-NEXT:  "params": {
> > +# CHECK-NEXT:    "diagnostics": [],
> > +# CHECK-NEXT:    "uri": "file:///main.cpp"
> > +# CHECK-NEXT:  }
> >  Content-Length: 58
> >
> >  {"jsonrpc":"2.0","id":2,"method":"shutdown","params":null}
> > -# CHECK: {"jsonrpc":"2.0","id":2,"result":null}
> >  Content-Length: 33
> >
> >  {"jsonrpc":"2.0":"method":"exit"}
> >
> > Modified: clang-tools-extra/trunk/test/clangd/diagnostics.test
> > URL: http://llvm.org/viewvc/llvm-project/clang-tools-
> > extra/trunk/test/clangd/diagnostics.test?rev=317486&r1=31748
> 5&r2=317486&view=d
> > iff
> > ============================================================
> ==================
> > --- clang-tools-extra/trunk/test/clangd/diagnostics.test (original)
> > +++ clang-tools-extra/trunk/test/clangd/diagnostics.test Mon Nov  6
> 07:40:30
> > 2017
> > @@ -1,4 +1,4 @@
> > -# RUN: clangd -run-synchronously < %s | FileCheck %s
> > +# RUN: clangd -pretty -run-synchronously < %s | FileCheck
> -strict-whitespace
> > %s
> >  # It is absolutely vital that this file has CRLF line endings.
> >  #
> >  Content-Length: 125
> > @@ -8,14 +8,43 @@ Content-Length: 125
> >  Content-Length: 152
> >
> >
> > {"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"
> textDocument":{"ur
> > i":"file:///foo.c","languageId":"c","version":1,"text":"void main()
> {}"}}}
> > -#
> > -# CHECK:
> > {"jsonrpc":"2.0","method":"textDocument/publishDiagnostics",
> "params":{"uri":"f
> > ile:///foo.c","diagnostics":[{"range":{"start": {"line": 0,
> "character": 1},
> > "end": {"line": 0, "character": 1}},"severity":2,"message":"return type
> of
> > 'main' is not 'int'"},{"range":{"start": {"line": 0, "character": 1},
> "end":
> > {"line": 0, "character": 1}},"severity":3,"message":"change return type
> to
> > 'int'"}]}}
> > -#
> > -#
> > +#      CHECK:  "method": "textDocument/publishDiagnostics",
> > +# CHECK-NEXT:  "params": {
> > +# CHECK-NEXT:    "diagnostics": [
> > +# CHECK-NEXT:      {
> > +# CHECK-NEXT:        "message": "return type of 'main' is not 'int'",
> > +# CHECK-NEXT:        "range": {
> > +# CHECK-NEXT:          "end": {
> > +# CHECK-NEXT:            "character": 1,
> > +# CHECK-NEXT:            "line": 0
> > +# CHECK-NEXT:          },
> > +# CHECK-NEXT:          "start": {
> > +# CHECK-NEXT:            "character": 1,
> > +# CHECK-NEXT:            "line": 0
> > +# CHECK-NEXT:          }
> > +# CHECK-NEXT:        },
> > +# CHECK-NEXT:        "severity": 2
> > +# CHECK-NEXT:      },
> > +# CHECK-NEXT:      {
> > +# CHECK-NEXT:        "message": "change return type to 'int'",
> > +# CHECK-NEXT:        "range": {
> > +# CHECK-NEXT:          "end": {
> > +# CHECK-NEXT:            "character": 1,
> > +# CHECK-NEXT:            "line": 0
> > +# CHECK-NEXT:          },
> > +# CHECK-NEXT:          "start": {
> > +# CHECK-NEXT:            "character": 1,
> > +# CHECK-NEXT:            "line": 0
> > +# CHECK-NEXT:          }
> > +# CHECK-NEXT:        },
> > +# CHECK-NEXT:        "severity": 3
> > +# CHECK-NEXT:      }
> > +# CHECK-NEXT:    ],
> > +# CHECK-NEXT:    "uri": "file:///foo.c"
> > +# CHECK-NEXT:  }
> >  Content-Length: 44
> >
> >  {"jsonrpc":"2.0","id":5,"method":"shutdown"}
> > -# CHECK: {"jsonrpc":"2.0","id":5,"result":null}
> >  Content-Length: 33
> >
> >  {"jsonrpc":"2.0":"method":"exit"}
> >
> > Modified: clang-tools-extra/trunk/test/clangd/did-change-watch-files.t
> est
> > URL: http://llvm.org/viewvc/llvm-project/clang-tools-
> > extra/trunk/test/clangd/did-change-watch-
> > files.test?rev=317486&r1=317485&r2=317486&view=diff
> > ============================================================
> ==================
> > --- clang-tools-extra/trunk/test/clangd/did-change-watch-files.test
> (original)
> > +++ clang-tools-extra/trunk/test/clangd/did-change-watch-files.test Mon
> Nov  6
> > 07:40:30 2017
> > @@ -5,18 +5,7 @@
> >  Content-Length: 143
> >
> >
> > {"jsonrpc":"2.0","id":0,"method":"initialize","params":{"pro
> cessId":123,"rootU
> > ri":"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
> > +# 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.c
> > pp","type":2},{"uri":"file:///path/to/file3.cpp","type":3}]}}
> >
> > Modified: clang-tools-extra/trunk/test/clangd/execute-command.test
> > URL: http://llvm.org/viewvc/llvm-project/clang-tools-
> > extra/trunk/test/clangd/execute-
> > command.test?rev=317486&r1=317485&r2=317486&view=diff
> > ============================================================
> ==================
> > --- clang-tools-extra/trunk/test/clangd/execute-command.test (original)
> > +++ clang-tools-extra/trunk/test/clangd/execute-command.test Mon Nov  6
> > 07:40:30 2017
> > @@ -1,4 +1,4 @@
> > -# RUN: clangd -run-synchronously < %s | FileCheck %s
> > +# RUN: clangd -pretty -run-synchronously < %s | FileCheck
> -strict-whitespace
> > %s
> >  # It is absolutely vital that this file has CRLF line endings.
> >  #
> >  Content-Length: 125
> > @@ -8,9 +8,54 @@ Content-Length: 125
> >  Content-Length: 180
> >
> >
> > {"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"
> textDocument":{"ur
> > i":"file:///foo.c","languageId":"c","version":1,"text":"int main(int i,
> char
> > **a) { if (i = 2) {}}"}}}
> > -#
> > -# CHECK:
> > {"jsonrpc":"2.0","method":"textDocument/publishDiagnostics",
> "params":{"uri":"f
> > ile:///foo.c","diagnostics":[{"range":{"start": {"line": 0,
> "character": 35},
> > "end": {"line": 0, "character": 35}},"severity":2,"message":"using the
> result
> > of an assignment as a condition without parentheses"},{"range":{"start":
> > {"line": 0, "character": 35}, "end": {"line": 0, "character":
> > 35}},"severity":3,"message":"place parentheses around the assignment to
> > silence this warning"},{"range":{"start": {"line": 0, "character": 35},
> "end":
> > {"line": 0, "character": 35}},"severity":3,"message":"use '==' to turn
> this
> > assignment into an equality comparison"}]}}
> > -#
> > +#      CHECK:  "method": "textDocument/publishDiagnostics",
> > +# CHECK-NEXT:  "params": {
> > +# CHECK-NEXT:    "diagnostics": [
> > +# CHECK-NEXT:      {
> > +# CHECK-NEXT:        "message": "using the result of an assignment as a
> > condition without parentheses",
> > +# CHECK-NEXT:        "range": {
> > +# CHECK-NEXT:          "end": {
> > +# CHECK-NEXT:            "character": 35,
> > +# CHECK-NEXT:            "line": 0
> > +# CHECK-NEXT:          },
> > +# CHECK-NEXT:          "start": {
> > +# CHECK-NEXT:            "character": 35,
> > +# CHECK-NEXT:            "line": 0
> > +# CHECK-NEXT:          }
> > +# CHECK-NEXT:        },
> > +# CHECK-NEXT:        "severity": 2
> > +# CHECK-NEXT:      },
> > +# CHECK-NEXT:      {
> > +# CHECK-NEXT:        "message": "place parentheses around the
> assignment to
> > silence this warning",
> > +# CHECK-NEXT:        "range": {
> > +# CHECK-NEXT:          "end": {
> > +# CHECK-NEXT:            "character": 35,
> > +# CHECK-NEXT:            "line": 0
> > +# CHECK-NEXT:          },
> > +# CHECK-NEXT:          "start": {
> > +# CHECK-NEXT:            "character": 35,
> > +# CHECK-NEXT:            "line": 0
> > +# CHECK-NEXT:          }
> > +# CHECK-NEXT:        },
> > +# CHECK-NEXT:        "severity": 3
> > +# CHECK-NEXT:      },
> > +# CHECK-NEXT:      {
> > +# CHECK-NEXT:        "message": "use '==' to turn this assignment into
> an
> > equality comparison",
> > +# CHECK-NEXT:        "range": {
> > +# CHECK-NEXT:          "end": {
> > +# CHECK-NEXT:            "character": 35,
> > +# CHECK-NEXT:            "line": 0
> > +# CHECK-NEXT:          },
> > +# CHECK-NEXT:          "start": {
> > +# CHECK-NEXT:            "character": 35,
> > +# CHECK-NEXT:            "line": 0
> > +# CHECK-NEXT:          }
> > +# CHECK-NEXT:        },
> > +# CHECK-NEXT:        "severity": 3
> > +# CHECK-NEXT:      }
> > +# CHECK-NEXT:    ],
> > +# CHECK-NEXT:    "uri": "file:///foo.c"
> > +# CHECK-NEXT:  }
> >  Content-Length: 72
> >
> >  {"jsonrpc":"2.0","id":3,"method":"workspace/executeCommand",
> "params":{}}
> >
> > Modified: clang-tools-extra/trunk/test/clangd/extra-flags.test
> > URL: http://llvm.org/viewvc/llvm-project/clang-tools-
> > extra/trunk/test/clangd/extra-
> > flags.test?rev=317486&r1=317485&r2=317486&view=diff
> > ============================================================
> ==================
> > --- clang-tools-extra/trunk/test/clangd/extra-flags.test (original)
> > +++ clang-tools-extra/trunk/test/clangd/extra-flags.test Mon Nov  6
> 07:40:30
> > 2017
> > @@ -1,4 +1,4 @@
> > -# RUN: clangd -run-synchronously < %s | FileCheck %s
> > +# RUN: clangd -pretty -run-synchronously < %s | FileCheck
> -strict-whitespace
> > %s
> >  # It is absolutely vital that this file has CRLF line endings.
> >  #
> >  Content-Length: 125
> > @@ -8,17 +8,80 @@ Content-Length: 125
> >  Content-Length: 205
> >
> >
> > {"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"
> textDocument":{"ur
> > i":"file:///foo.c","languageId":"c","version":1,"text":"int main() {
> int i;
> > return i; }"},"metadata":{"extraFlags":["-Wall"]}}}
> > -# CHECK:
> > {"jsonrpc":"2.0","method":"textDocument/publishDiagnostics",
> "params":{"uri":"f
> > ile:///foo.c","diagnostics":[{"range":{"start": {"line": 0,
> "character": 28},
> > "end": {"line": 0, "character": 28}},"severity":2,"message":"variable
> 'i' is
> > uninitialized when used here"},{"range":{"start": {"line": 0,
> "character":
> > 19}, "end": {"line": 0, "character": 19}},"severity":3,"message":"i
> nitialize
> > the variable 'i' to silence this warning"}]}}
> > -#
> > +#      CHECK:  "method": "textDocument/publishDiagnostics",
> > +# CHECK-NEXT:  "params": {
> > +# CHECK-NEXT:    "diagnostics": [
> > +# CHECK-NEXT:      {
> > +# CHECK-NEXT:        "message": "variable 'i' is uninitialized when used
> > here",
> > +# CHECK-NEXT:        "range": {
> > +# CHECK-NEXT:          "end": {
> > +# CHECK-NEXT:            "character": 28,
> > +# CHECK-NEXT:            "line": 0
> > +# CHECK-NEXT:          },
> > +# CHECK-NEXT:          "start": {
> > +# CHECK-NEXT:            "character": 28,
> > +# CHECK-NEXT:            "line": 0
> > +# CHECK-NEXT:          }
> > +# CHECK-NEXT:        },
> > +# CHECK-NEXT:        "severity": 2
> > +# CHECK-NEXT:      },
> > +# CHECK-NEXT:      {
> > +# CHECK-NEXT:        "message": "initialize the variable 'i' to silence
> this
> > warning",
> > +# CHECK-NEXT:        "range": {
> > +# CHECK-NEXT:          "end": {
> > +# CHECK-NEXT:            "character": 19,
> > +# CHECK-NEXT:            "line": 0
> > +# CHECK-NEXT:          },
> > +# CHECK-NEXT:          "start": {
> > +# CHECK-NEXT:            "character": 19,
> > +# CHECK-NEXT:            "line": 0
> > +# CHECK-NEXT:          }
> > +# CHECK-NEXT:        },
> > +# CHECK-NEXT:        "severity": 3
> > +# CHECK-NEXT:      }
> > +# CHECK-NEXT:    ],
> > +# CHECK-NEXT:    "uri": "file:///foo.c"
> > +# CHECK-NEXT:  }
> >  Content-Length: 175
> >
> >
> > {"jsonrpc":"2.0","method":"textDocument/didChange","params":
> {"textDocument":{"
> > uri":"file:///foo.c","version":2},"contentChanges":[{"text":"int main()
> { int
> > i; return i; }"}]}}
> > -# CHECK:
> > {"jsonrpc":"2.0","method":"textDocument/publishDiagnostics",
> "params":{"uri":"f
> > ile:///foo.c","diagnostics":[{"range":{"start": {"line": 0,
> "character": 28},
> > "end": {"line": 0, "character": 28}},"severity":2,"message":"variable
> 'i' is
> > uninitialized when used here"},{"range":{"start": {"line": 0,
> "character":
> > 19}, "end": {"line": 0, "character": 19}},"severity":3,"message":"i
> nitialize
> > the variable 'i' to silence this warning"}]}}
> > -#
> > +#      CHECK:  "method": "textDocument/publishDiagnostics",
> > +# CHECK-NEXT:  "params": {
> > +# CHECK-NEXT:    "diagnostics": [
> > +# CHECK-NEXT:      {
> > +# CHECK-NEXT:        "message": "variable 'i' is uninitialized when used
> > here",
> > +# CHECK-NEXT:        "range": {
> > +# CHECK-NEXT:          "end": {
> > +# CHECK-NEXT:            "character": 28,
> > +# CHECK-NEXT:            "line": 0
> > +# CHECK-NEXT:          },
> > +# CHECK-NEXT:          "start": {
> > +# CHECK-NEXT:            "character": 28,
> > +# CHECK-NEXT:            "line": 0
> > +# CHECK-NEXT:          }
> > +# CHECK-NEXT:        },
> > +# CHECK-NEXT:        "severity": 2
> > +# CHECK-NEXT:      },
> > +# CHECK-NEXT:      {
> > +# CHECK-NEXT:        "message": "initialize the variable 'i' to silence
> this
> > warning",
> > +# CHECK-NEXT:        "range": {
> > +# CHECK-NEXT:          "end": {
> > +# CHECK-NEXT:            "character": 19,
> > +# CHECK-NEXT:            "line": 0
> > +# CHECK-NEXT:          },
> > +# CHECK-NEXT:          "start": {
> > +# CHECK-NEXT:            "character": 19,
> > +# CHECK-NEXT:            "line": 0
> > +# CHECK-NEXT:          }
> > +# CHECK-NEXT:        },
> > +# CHECK-NEXT:        "severity": 3
> > +# CHECK-NEXT:      }
> > +# CHECK-NEXT:    ],
> > +# CHECK-NEXT:    "uri": "file:///foo.c"
> > +# CHECK-NEXT:  }
> >  Content-Length: 44
> >
> >  {"jsonrpc":"2.0","id":5,"method":"shutdown"}
> > -# CHECK: {"jsonrpc":"2.0","id":5,"result":null}
> >  Content-Length: 33
> >
> >  {"jsonrpc":"2.0":"method":"exit"}
> >
> > Modified: clang-tools-extra/trunk/test/clangd/fixits.test
> > URL: http://llvm.org/viewvc/llvm-project/clang-tools-
> > extra/trunk/test/clangd/fixits.test?rev=317486&r1=317485&r2=
> 317486&view=diff
> > ============================================================
> ==================
> > --- clang-tools-extra/trunk/test/clangd/fixits.test (original)
> > +++ clang-tools-extra/trunk/test/clangd/fixits.test Mon Nov  6 07:40:30
> 2017
> > @@ -1,4 +1,4 @@
> > -# RUN: clangd -run-synchronously < %s | FileCheck %s
> > +# RUN: clangd -pretty -run-synchronously < %s | FileCheck
> -strict-whitespace
> > %s
> >  # It is absolutely vital that this file has CRLF line endings.
> >  #
> >  Content-Length: 125
> > @@ -8,30 +8,242 @@ Content-Length: 125
> >  Content-Length: 180
> >
> >
> > {"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"
> textDocument":{"ur
> > i":"file:///foo.c","languageId":"c","version":1,"text":"int main(int i,
> char
> > **a) { if (i = 2) {}}"}}}
> > -#
> > -# CHECK:
> > {"jsonrpc":"2.0","method":"textDocument/publishDiagnostics",
> "params":{"uri":"f
> > ile:///foo.c","diagnostics":[{"range":{"start": {"line": 0,
> "character": 35},
> > "end": {"line": 0, "character": 35}},"severity":2,"message":"using the
> result
> > of an assignment as a condition without parentheses"},{"range":{"start":
> > {"line": 0, "character": 35}, "end": {"line": 0, "character":
> > 35}},"severity":3,"message":"place parentheses around the assignment to
> > silence this warning"},{"range":{"start": {"line": 0, "character": 35},
> "end":
> > {"line": 0, "character": 35}},"severity":3,"message":"use '==' to turn
> this
> > assignment into an equality comparison"}]}}
> > -#
> > +#      CHECK:    "method": "textDocument/publishDiagnostics",
> > +# CHECK-NEXT:  "params": {
> > +# CHECK-NEXT:    "diagnostics": [
> > +# CHECK-NEXT:      {
> > +# CHECK-NEXT:        "message": "using the result of an assignment as a
> > condition without parentheses",
> > +# CHECK-NEXT:        "range": {
> > +# CHECK-NEXT:          "end": {
> > +# CHECK-NEXT:            "character": 35,
> > +# CHECK-NEXT:            "line": 0
> > +# CHECK-NEXT:          },
> > +# CHECK-NEXT:          "start": {
> > +# CHECK-NEXT:            "character": 35,
> > +# CHECK-NEXT:            "line": 0
> > +# CHECK-NEXT:          }
> > +# CHECK-NEXT:        },
> > +# CHECK-NEXT:        "severity": 2
> > +# CHECK-NEXT:      },
> > +# CHECK-NEXT:      {
> > +# CHECK-NEXT:        "message": "place parentheses around the
> assignment to
> > silence this warning",
> > +# CHECK-NEXT:        "range": {
> > +# CHECK-NEXT:          "end": {
> > +# CHECK-NEXT:            "character": 35,
> > +# CHECK-NEXT:            "line": 0
> > +# CHECK-NEXT:          },
> > +# CHECK-NEXT:          "start": {
> > +# CHECK-NEXT:            "character": 35,
> > +# CHECK-NEXT:            "line": 0
> > +# CHECK-NEXT:          }
> > +# CHECK-NEXT:        },
> > +# CHECK-NEXT:        "severity": 3
> > +# CHECK-NEXT:      },
> > +# CHECK-NEXT:      {
> > +# CHECK-NEXT:        "message": "use '==' to turn this assignment into
> an
> > equality comparison",
> > +# CHECK-NEXT:        "range": {
> > +# CHECK-NEXT:          "end": {
> > +# CHECK-NEXT:            "character": 35,
> > +# CHECK-NEXT:            "line": 0
> > +# CHECK-NEXT:          },
> > +# CHECK-NEXT:          "start": {
> > +# CHECK-NEXT:            "character": 35,
> > +# CHECK-NEXT:            "line": 0
> > +# CHECK-NEXT:          }
> > +# CHECK-NEXT:        },
> > +# CHECK-NEXT:        "severity": 3
> > +# CHECK-NEXT:      }
> > +# CHECK-NEXT:    ],
> > +# CHECK-NEXT:    "uri": "file:///foo.c"
> > +# CHECK-NEXT:  }
> >  Content-Length: 746
> >
> >
> > {"jsonrpc":"2.0","id":2,"method":"textDocument/codeAction","
> params":{"textDocu
> > ment":{"uri":"file:///foo.c"},"range":{"start":{"line":104,"
> character":13},"en
> > d":{"line":0,"character":35}},"context":{"diagnostics":[{"ra
> nge":{"start":
> > {"line": 0, "character": 35}, "end": {"line": 0, "character":
> > 35}},"severity":2,"message":"using the result of an assignment as a
> condition
> > without parentheses"},{"range":{"start": {"line": 0, "character": 35},
> "end":
> > {"line": 0, "character": 35}},"severity":3,"message":"place parentheses
> around
> > the assignment to silence this warning"},{"range":{"start": {"line": 0,
> > "character": 35}, "end": {"line": 0, "character":
> > 35}},"severity":3,"message":"use '==' to turn this assignment into an
> equality
> > comparison"}]}}}
> > -#
> > -# CHECK: {"jsonrpc":"2.0","id":2,"result":[{"title":"Apply FixIt 'place
> > parentheses around the assignment to silence this warning'", "command":
> > "clangd.applyFix", "arguments": [{"changes": {"file:///foo.c": [{"range":
> > {"start": {"line": 0, "character": 32}, "end": {"line": 0, "character":
> 32}},
> > "newText": "("},{"range": {"start": {"line": 0, "character": 37}, "end":
> > {"line": 0, "character": 37}}, "newText": ")"}]}}]},{"title":"Apply
> FixIt 'use
> > '==' to turn this assignment into an equality comparison'", "command":
> > "clangd.applyFix", "arguments": [{"changes": {"file:///foo.c": [{"range":
> > {"start": {"line": 0, "character": 34}, "end": {"line": 0, "character":
> 35}},
> > "newText": "=="}]}}]}]}
> > -#
> > +#      CHECK:  "id": 2,
> > +# CHECK-NEXT:  "jsonrpc": "2.0",
> > +# CHECK-NEXT:  "result": [
> > +# CHECK-NEXT:    {
> > +# CHECK-NEXT:      "arguments": [
> > +# CHECK-NEXT:        {
> > +# CHECK-NEXT:          "changes": {
> > +# CHECK-NEXT:            "file:///foo.c": [
> > +# CHECK-NEXT:              {
> > +# CHECK-NEXT:                "newText": "(",
> > +# CHECK-NEXT:                "range": {
> > +# CHECK-NEXT:                  "end": {
> > +# CHECK-NEXT:                    "character": 32,
> > +# CHECK-NEXT:                    "line": 0
> > +# CHECK-NEXT:                  },
> > +# CHECK-NEXT:                  "start": {
> > +# CHECK-NEXT:                    "character": 32,
> > +# CHECK-NEXT:                    "line": 0
> > +# CHECK-NEXT:                  }
> > +# CHECK-NEXT:                }
> > +# CHECK-NEXT:              },
> > +# CHECK-NEXT:              {
> > +# CHECK-NEXT:                "newText": ")",
> > +# CHECK-NEXT:                "range": {
> > +# CHECK-NEXT:                  "end": {
> > +# CHECK-NEXT:                    "character": 37,
> > +# CHECK-NEXT:                    "line": 0
> > +# CHECK-NEXT:                  },
> > +# CHECK-NEXT:                  "start": {
> > +# CHECK-NEXT:                    "character": 37,
> > +# CHECK-NEXT:                    "line": 0
> > +# CHECK-NEXT:                  }
> > +# CHECK-NEXT:                }
> > +# CHECK-NEXT:              }
> > +# CHECK-NEXT:            ]
> > +# CHECK-NEXT:          }
> > +# CHECK-NEXT:        }
> > +# CHECK-NEXT:      ],
> > +# CHECK-NEXT:      "command": "clangd.applyFix",
> > +# CHECK-NEXT:      "title": "Apply FixIt place parentheses around the
> > assignment to silence this warning"
> > +# CHECK-NEXT:    },
> > +# CHECK-NEXT:    {
> > +# CHECK-NEXT:      "arguments": [
> > +# CHECK-NEXT:        {
> > +# CHECK-NEXT:          "changes": {
> > +# CHECK-NEXT:            "file:///foo.c": [
> > +# CHECK-NEXT:              {
> > +# CHECK-NEXT:                "newText": "==",
> > +# CHECK-NEXT:                "range": {
> > +# CHECK-NEXT:                  "end": {
> > +# CHECK-NEXT:                    "character": 35,
> > +# CHECK-NEXT:                    "line": 0
> > +# CHECK-NEXT:                  },
> > +# CHECK-NEXT:                  "start": {
> > +# CHECK-NEXT:                    "character": 34,
> > +# CHECK-NEXT:                    "line": 0
> > +# CHECK-NEXT:                  }
> > +# CHECK-NEXT:                }
> > +# CHECK-NEXT:              }
> > +# CHECK-NEXT:            ]
> > +# CHECK-NEXT:          }
> > +# CHECK-NEXT:        }
> > +# CHECK-NEXT:      ],
> > +# CHECK-NEXT:      "command": "clangd.applyFix",
> > +# CHECK-NEXT:      "title": "Apply FixIt use '==' to turn this
> assignment
> > into an equality comparison"
> > +# CHECK-NEXT:    }
> > +# CHECK-NEXT:  ]
> >  Content-Length: 771
> >
> > -
> > {"jsonrpc":"2.0","id":2,"method":"textDocument/codeAction","
> params":{"textDocu
> > ment":{"uri":"file:///foo.c"},"range":{"start":{"line":104,"
> character":13},"en
> > d":{"line":0,"character":35}},"context":{"diagnostics":[{"ra
> nge":{"start":
> > {"line": 0, "character": 35}, "end": {"line": 0, "character":
> > 35}},"severity":2,"code":"1","source":"foo","message":"using the result
> of an
> > assignment as a condition without parentheses"},{"range":{"start":
> {"line": 0,
> > "character": 35}, "end": {"line": 0, "character":
> > 35}},"severity":3,"message":"place parentheses around the assignment to
> > silence this warning"},{"range":{"start": {"line": 0, "character": 35},
> "end":
> > {"line": 0, "character": 35}},"severity":3,"message":"use '==' to turn
> this
> > assignment into an equality comparison"}]}}}
> > +{"jsonrpc":"2.0","id":3,"method":"textDocument/codeAction",
> "params":{"textDoc
> > ument":{"uri":"file:///foo.c"},"range":{"start":{"line":104,
> "character":13},"e
> > nd":{"line":0,"character":35}},"context":{"diagnostics":[{"r
> ange":{"start":
> > {"line": 0, "character": 35}, "end": {"line": 0, "character":
> > 35}},"severity":2,"code":"1","source":"foo","message":"using the result
> of an
> > assignment as a condition without parentheses"},{"range":{"start":
> {"line": 0,
> > "character": 35}, "end": {"line": 0, "character":
> > 35}},"severity":3,"message":"place parentheses around the assignment to
> > silence this warning"},{"range":{"start": {"line": 0, "character": 35},
> "end":
> > {"line": 0, "character": 35}},"severity":3,"message":"use '==' to turn
> this
> > assignment into an equality comparison"}]}}}
> >  # Make sure unused "code" and "source" fields ignored gracefully
> > -# CHECK: {"jsonrpc":"2.0","id":2,"result":[{"title":"Apply FixIt 'place
> > parentheses around the assignment to silence this warning'", "command":
> > "clangd.applyFix", "arguments": [{"changes": {"file:///foo.c": [{"range":
> > {"start": {"line": 0, "character": 32}, "end": {"line": 0, "character":
> 32}},
> > "newText": "("},{"range": {"start": {"line": 0, "character": 37}, "end":
> > {"line": 0, "character": 37}}, "newText": ")"}]}}]},{"title":"Apply
> FixIt 'use
> > '==' to turn this assignment into an equality comparison'", "command":
> > "clangd.applyFix", "arguments": [{"changes": {"file:///foo.c": [{"range":
> > {"start": {"line": 0, "character": 34}, "end": {"line": 0, "character":
> 35}},
> > "newText": "=="}]}}]}]}
> > -#
> > +#      CHECK:  "id": 3,
> > +# CHECK-NEXT:  "jsonrpc": "2.0",
> > +# CHECK-NEXT:  "result": [
> > +# CHECK-NEXT:    {
> > +# CHECK-NEXT:      "arguments": [
> > +# CHECK-NEXT:        {
> > +# CHECK-NEXT:          "changes": {
> > +# CHECK-NEXT:            "file:///foo.c": [
> > +# CHECK-NEXT:              {
> > +# CHECK-NEXT:                "newText": "(",
> > +# CHECK-NEXT:                "range": {
> > +# CHECK-NEXT:                  "end": {
> > +# CHECK-NEXT:                    "character": 32,
> > +# CHECK-NEXT:                    "line": 0
> > +# CHECK-NEXT:                  },
> > +# CHECK-NEXT:                  "start": {
> > +# CHECK-NEXT:                    "character": 32,
> > +# CHECK-NEXT:                    "line": 0
> > +# CHECK-NEXT:                  }
> > +# CHECK-NEXT:                }
> > +# CHECK-NEXT:              },
> > +# CHECK-NEXT:              {
> > +# CHECK-NEXT:                "newText": ")",
> > +# CHECK-NEXT:                "range": {
> > +# CHECK-NEXT:                  "end": {
> > +# CHECK-NEXT:                    "character": 37,
> > +# CHECK-NEXT:                    "line": 0
> > +# CHECK-NEXT:                  },
> > +# CHECK-NEXT:                  "start": {
> > +# CHECK-NEXT:                    "character": 37,
> > +# CHECK-NEXT:                    "line": 0
> > +# CHECK-NEXT:                  }
> > +# CHECK-NEXT:                }
> > +# CHECK-NEXT:              }
> > +# CHECK-NEXT:            ]
> > +# CHECK-NEXT:          }
> > +# CHECK-NEXT:        }
> > +# CHECK-NEXT:      ],
> > +# CHECK-NEXT:      "command": "clangd.applyFix",
> > +# CHECK-NEXT:      "title": "Apply FixIt place parentheses around the
> > assignment to silence this warning"
> > +# CHECK-NEXT:    },
> > +# CHECK-NEXT:    {
> > +# CHECK-NEXT:      "arguments": [
> > +# CHECK-NEXT:        {
> > +# CHECK-NEXT:          "changes": {
> > +# CHECK-NEXT:            "file:///foo.c": [
> > +# CHECK-NEXT:              {
> > +# CHECK-NEXT:                "newText": "==",
> > +# CHECK-NEXT:                "range": {
> > +# CHECK-NEXT:                  "end": {
> > +# CHECK-NEXT:                    "character": 35,
> > +# CHECK-NEXT:                    "line": 0
> > +# CHECK-NEXT:                  },
> > +# CHECK-NEXT:                  "start": {
> > +# CHECK-NEXT:                    "character": 34,
> > +# CHECK-NEXT:                    "line": 0
> > +# CHECK-NEXT:                  }
> > +# CHECK-NEXT:                }
> > +# CHECK-NEXT:              }
> > +# CHECK-NEXT:            ]
> > +# CHECK-NEXT:          }
> > +# CHECK-NEXT:        }
> > +# CHECK-NEXT:      ],
> > +# CHECK-NEXT:      "command": "clangd.applyFix",
> > +# CHECK-NEXT:      "title": "Apply FixIt use '==' to turn this
> assignment
> > into an equality comparison"
> > +# CHECK-NEXT:    }
> > +# CHECK-NEXT:  ]
> >  Content-Length: 329
> >
> > -
> > {"jsonrpc":"2.0","id":3,"method":"workspace/executeCommand",
> "params":{"command
> > ":"clangd.applyFix","arguments":[{"changes":{"file:///foo.c"
> :[{"range":{"start
> > ":{"line":0,"character":32},"end":{"line":0,"character":32}}
> ,"newText":"("},{"
> > range":{"start":{"line":0,"character":37},"end":{"line":0,"c
> haracter":37}},"ne
> > wText":")"}]}}]}}
> > -# CHECK: {"jsonrpc":"2.0","id":3,"result":"Fix applied."}
> > -# CHECK:
> > {"jsonrpc":"2.0","id":1,"method":"workspace/applyEdit","params":{"edit":
> > {"changes": {"file:///foo.c": [{"range": {"start": {"line": 0,
> "character":
> > 32}, "end": {"line": 0, "character": 32}}, "newText": "("},{"range":
> {"start":
> > {"line": 0, "character": 37}, "end": {"line": 0, "character": 37}},
> "newText":
> > ")"}]}}}}
> > +{"jsonrpc":"2.0","id":4,"method":"workspace/executeCommand"
> ,"params":{"comman
> > d":"clangd.applyFix","arguments":[{"changes":{"file:///foo.c
> ":[{"range":{"star
> > t":{"line":0,"character":32},"end":{"line":0,"character":32}
> },"newText":"("},{
> > "range":{"start":{"line":0,"character":37},"end":{"line":0,"
> character":37}},"n
> > ewText":")"}]}}]}}
> > +#      CHECK:  "id": 4,
> > +# CHECK-NEXT:  "jsonrpc": "2.0",
> > +# CHECK-NEXT:  "result": "Fix applied."
> > +#
> > +#      CHECK:  "id": 1,
> > +# CHECK-NEXT:  "jsonrpc": "2.0",
> > +# CHECK-NEXT:  "method": "workspace/applyEdit",
> > +# CHECK-NEXT:  "params": {
> > +# CHECK-NEXT:    "edit": {
> > +# CHECK-NEXT:      "changes": {
> > +# CHECK-NEXT:        "file:///foo.c": [
> > +# CHECK-NEXT:          {
> > +# CHECK-NEXT:            "newText": "(",
> > +# CHECK-NEXT:            "range": {
> > +# CHECK-NEXT:              "end": {
> > +# CHECK-NEXT:                "character": 32,
> > +# CHECK-NEXT:                "line": 0
> > +# CHECK-NEXT:              },
> > +# CHECK-NEXT:              "start": {
> > +# CHECK-NEXT:                "character": 32,
> > +# CHECK-NEXT:                "line": 0
> > +# CHECK-NEXT:              }
> > +# CHECK-NEXT:            }
> > +# CHECK-NEXT:          },
> > +# CHECK-NEXT:          {
> > +# CHECK-NEXT:            "newText": ")",
> > +# CHECK-NEXT:            "range": {
> > +# CHECK-NEXT:              "end": {
> > +# CHECK-NEXT:                "character": 37,
> > +# CHECK-NEXT:                "line": 0
> > +# CHECK-NEXT:              },
> > +# CHECK-NEXT:              "start": {
> > +# CHECK-NEXT:                "character": 37,
> > +# CHECK-NEXT:                "line": 0
> > +# CHECK-NEXT:              }
> > +# CHECK-NEXT:            }
> > +# CHECK-NEXT:          }
> > +# CHECK-NEXT:        ]
> > +# CHECK-NEXT:      }
> > +# CHECK-NEXT:    }
> > +# CHECK-NEXT:  }
> >  Content-Length: 44
> >
> > -{"jsonrpc":"2.0","id":3,"method":"shutdown"}
> > -# CHECK: {"jsonrpc":"2.0","id":3,"result":null}
> > +{"jsonrpc":"2.0","id":4,"method":"shutdown"}
> >  Content-Length: 33
> >
> >  {"jsonrpc":"2.0":"method":"exit"}
> >
> > Modified: clang-tools-extra/trunk/test/clangd/formatting.test
> > URL: http://llvm.org/viewvc/llvm-project/clang-tools-
> > extra/trunk/test/clangd/formatting.test?rev=317486&r1=317485
> &r2=317486&view=di
> > ff
> > ============================================================
> ==================
> > --- clang-tools-extra/trunk/test/clangd/formatting.test (original)
> > +++ clang-tools-extra/trunk/test/clangd/formatting.test Mon Nov  6
> 07:40:30
> > 2017
> > @@ -1,30 +1,71 @@
> > -# RUN: clangd < %s | FileCheck %s
> > +# RUN: clangd -pretty -run-synchronously < %s | FileCheck
> -strict-whitespace
> > %s
> >  # It is absolutely vital that this file has CRLF line endings.
> >  #
> >  Content-Length: 125
> >
> >
> > {"jsonrpc":"2.0","id":0,"method":"initialize","params":{"pro
> cessId":123,"rootP
> > ath":"clangd","capabilities":{},"trace":"off"}}
> > -# 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: }}}
> > -#
> >  Content-Length: 193
> >
> >
> > {"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"
> textDocument":{"ur
> > i":"file:///foo.c","languageId":"c","version":1,"text":"int foo ( int x
> ) {\n
> > x = x+1;\n    return x;\n    }"}}}
> > -#
> > -#
> >  Content-Length: 233
> >
> >
> > {"jsonrpc":"2.0","id":1,"method":"textDocument/rangeFormatti
> ng","params":{"tex
> > tDocument":{"uri":"file:///foo.c"},"range":{"start":{"line":
> 1,"character":4},"
> > end":{"line":1,"character":12}},"options":{"tabSize":4,"inse
> rtSpaces":true}}}
> > -# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"range": {"start":
> {"line": 0,
> > "character": 19}, "end": {"line": 1, "character": 4}}, "newText": "\n
> > "},{"range": {"start": {"line": 1, "character": 9}, "end": {"line": 1,
> > "character": 9}}, "newText": " "},{"range": {"start": {"line": 1,
> "character":
> > 10}, "end": {"line": 1, "character": 10}}, "newText": " "},{"range":
> {"start":
> > {"line": 1, "character": 12}, "end": {"line": 2, "character": 4}},
> "newText":
> > "\n  "}]}
> > -#
> > -#
> > +#      CHECK:   "id": 1,
> > +# CHECK-NEXT:  "jsonrpc": "2.0",
> > +# CHECK-NEXT:  "result": [
> > +# CHECK-NEXT:    {
> > +# CHECK-NEXT:      "newText": "\n  ",
> > +# CHECK-NEXT:      "range": {
> > +# CHECK-NEXT:        "end": {
> > +# CHECK-NEXT:          "character": 4,
> > +# CHECK-NEXT:          "line": 1
> > +# CHECK-NEXT:        },
> > +# CHECK-NEXT:        "start": {
> > +# CHECK-NEXT:          "character": 19,
> > +# CHECK-NEXT:          "line": 0
> > +# CHECK-NEXT:        }
> > +# CHECK-NEXT:      }
> > +# CHECK-NEXT:    },
> > +# CHECK-NEXT:    {
> > +# CHECK-NEXT:      "newText": " ",
> > +# CHECK-NEXT:      "range": {
> > +# CHECK-NEXT:        "end": {
> > +# CHECK-NEXT:          "character": 9,
> > +# CHECK-NEXT:          "line": 1
> > +# CHECK-NEXT:        },
> > +# CHECK-NEXT:        "start": {
> > +# CHECK-NEXT:          "character": 9,
> > +# CHECK-NEXT:          "line": 1
> > +# CHECK-NEXT:        }
> > +# CHECK-NEXT:      }
> > +# CHECK-NEXT:    },
> > +# CHECK-NEXT:    {
> > +# CHECK-NEXT:      "newText": " ",
> > +# CHECK-NEXT:      "range": {
> > +# CHECK-NEXT:        "end": {
> > +# CHECK-NEXT:          "character": 10,
> > +# CHECK-NEXT:          "line": 1
> > +# CHECK-NEXT:        },
> > +# CHECK-NEXT:        "start": {
> > +# CHECK-NEXT:          "character": 10,
> > +# CHECK-NEXT:          "line": 1
> > +# CHECK-NEXT:        }
> > +# CHECK-NEXT:      }
> > +# CHECK-NEXT:    },
> > +# CHECK-NEXT:    {
> > +# CHECK-NEXT:      "newText": "\n  ",
> > +# CHECK-NEXT:      "range": {
> > +# CHECK-NEXT:        "end": {
> > +# CHECK-NEXT:          "character": 4,
> > +# CHECK-NEXT:          "line": 2
> > +# CHECK-NEXT:        },
> > +# CHECK-NEXT:        "start": {
> > +# CHECK-NEXT:          "character": 12,
> > +# CHECK-NEXT:          "line": 1
> > +# CHECK-NEXT:        }
> > +# CHECK-NEXT:      }
> > +# CHECK-NEXT:    }
> > +# CHECK-NEXT:  ]
> >  Content-Length: 197
> >
> >
> > {"jsonrpc":"2.0","method":"textDocument/didChange","params":
> {"textDocument":{"
> > uri":"file:///foo.c","version":5},"contentChanges":[{"text":"int foo (
> int x )
> > {\n  x = x + 1;\n  return x;\n    }"}]}}
> > @@ -33,14 +74,68 @@ Content-Length: 197
> >  Content-Length: 233
> >
> >
> > {"jsonrpc":"2.0","id":2,"method":"textDocument/rangeFormatti
> ng","params":{"tex
> > tDocument":{"uri":"file:///foo.c"},"range":{"start":{"line":
> 1,"character":2},"
> > end":{"line":1,"character":12}},"options":{"tabSize":4,"inse
> rtSpaces":true}}}
> > -# CHECK: {"jsonrpc":"2.0","id":2,"result":[]}
> > -#
> > +#      CHECK:  "id": 2,
> > +# CHECK-NEXT:  "jsonrpc": "2.0",
> > +# CHECK-NEXT:  "result": []
> >  Content-Length: 153
> >
> >
> > {"jsonrpc":"2.0","id":3,"method":"textDocument/formatting","
> params":{"textDocu
> > ment":{"uri":"file:///foo.c"},"options":{"tabSize":4,"insert
> Spaces":true}}}
> > -# CHECK: {"jsonrpc":"2.0","id":3,"result":[{"range": {"start":
> {"line": 0,
> > "character": 7}, "end": {"line": 0, "character": 8}}, "newText":
> ""},{"range":
> > {"start": {"line": 0, "character": 9}, "end": {"line": 0, "character":
> 10}},
> > "newText": ""},{"range": {"start": {"line": 0, "character": 15}, "end":
> > {"line": 0, "character": 16}}, "newText": ""},{"range": {"start":
> {"line": 2,
> > "character": 11}, "end": {"line": 3, "character": 4}}, "newText": "\n"}]}
> > -#
> > -#
> > +#      CHECK:  "id": 3,
> > +# CHECK-NEXT:  "jsonrpc": "2.0",
> > +# CHECK-NEXT:  "result": [
> > +# CHECK-NEXT:    {
> > +# CHECK-NEXT:      "newText": "",
> > +# CHECK-NEXT:      "range": {
> > +# CHECK-NEXT:        "end": {
> > +# CHECK-NEXT:          "character": 8,
> > +# CHECK-NEXT:          "line": 0
> > +# CHECK-NEXT:        },
> > +# CHECK-NEXT:        "start": {
> > +# CHECK-NEXT:          "character": 7,
> > +# CHECK-NEXT:          "line": 0
> > +# CHECK-NEXT:        }
> > +# CHECK-NEXT:      }
> > +# CHECK-NEXT:    },
> > +# CHECK-NEXT:    {
> > +# CHECK-NEXT:      "newText": "",
> > +# CHECK-NEXT:      "range": {
> > +# CHECK-NEXT:        "end": {
> > +# CHECK-NEXT:          "character": 10,
> > +# CHECK-NEXT:          "line": 0
> > +# CHECK-NEXT:        },
> > +# CHECK-NEXT:        "start": {
> > +# CHECK-NEXT:          "character": 9,
> > +# CHECK-NEXT:          "line": 0
> > +# CHECK-NEXT:        }
> > +# CHECK-NEXT:      }
> > +# CHECK-NEXT:    },
> > +# CHECK-NEXT:    {
> > +# CHECK-NEXT:      "newText": "",
> > +# CHECK-NEXT:      "range": {
> > +# CHECK-NEXT:        "end": {
> > +# CHECK-NEXT:          "character": 16,
> > +# CHECK-NEXT:          "line": 0
> > +# CHECK-NEXT:        },
> > +# CHECK-NEXT:        "start": {
> > +# CHECK-NEXT:          "character": 15,
> > +# CHECK-NEXT:          "line": 0
> > +# CHECK-NEXT:        }
> > +# CHECK-NEXT:      }
> > +# CHECK-NEXT:    },
> > +# CHECK-NEXT:    {
> > +# CHECK-NEXT:      "newText": "\n",
> > +# CHECK-NEXT:      "range": {
> > +# CHECK-NEXT:        "end": {
> > +# CHECK-NEXT:          "character": 4,
> > +# CHECK-NEXT:          "line": 3
> > +# CHECK-NEXT:        },
> > +# CHECK-NEXT:        "start": {
> > +# CHECK-NEXT:          "character": 11,
> > +# CHECK-NEXT:          "line": 2
> > +# CHECK-NEXT:        }
> > +# CHECK-NEXT:      }
> > +# CHECK-NEXT:    }
> > +# CHECK-NEXT:  ]
> >  Content-Length: 190
> >
> >
> > {"jsonrpc":"2.0","method":"textDocument/didChange","params":
> {"textDocument":{"
> > uri":"file:///foo.c","version":9},"contentChanges":[{"text":"int
> foo(int x)
> > {\n  x = x + 1;\n  return x;\n}"}]}}
> > @@ -49,8 +144,9 @@ Content-Length: 190
> >  Content-Length: 153
> >
> >
> > {"jsonrpc":"2.0","id":4,"method":"textDocument/formatting","
> params":{"textDocu
> > ment":{"uri":"file:///foo.c"},"options":{"tabSize":4,"insert
> Spaces":true}}}
> > -# CHECK: {"jsonrpc":"2.0","id":4,"result":[]}
> > -#
> > +#      CHECK:  "id": 4,
> > +# CHECK-NEXT:  "jsonrpc": "2.0",
> > +# CHECK-NEXT:  "result": []
> >  Content-Length: 193
> >
> >
> > {"jsonrpc":"2.0","method":"textDocument/didChange","params":
> {"textDocument":{"
> > uri":"file:///foo.c","version":5},"contentChanges":[{"text":"int foo (
> int x )
> > {\n  x = x + 1;\n  return x;\n}"}]}}
> > @@ -59,13 +155,53 @@ Content-Length: 193
> >  Content-Length: 204
> >
> >
> > {"jsonrpc":"2.0","id":5,"method":"textDocument/onTypeFormatt
> ing","params":{"te
> > xtDocument":{"uri":"file:///foo.c"},"position":{"line":3,"ch
> aracter":1},"ch":"
> > }","options":{"tabSize":4,"insertSpaces":true}}}
> > -# CHECK: {"jsonrpc":"2.0","id":5,"result":[{"range": {"start":
> {"line": 0,
> > "character": 7}, "end": {"line": 0, "character": 8}}, "newText":
> ""},{"range":
> > {"start": {"line": 0, "character": 9}, "end": {"line": 0, "character":
> 10}},
> > "newText": ""},{"range": {"start": {"line": 0, "character": 15}, "end":
> > {"line": 0, "character": 16}}, "newText": ""}]}
> > -#
> > +#      CHECK:  "id": 5,
> > +# CHECK-NEXT:  "jsonrpc": "2.0",
> > +# CHECK-NEXT:  "result": [
> > +# CHECK-NEXT:    {
> > +# CHECK-NEXT:      "newText": "",
> > +# CHECK-NEXT:      "range": {
> > +# CHECK-NEXT:        "end": {
> > +# CHECK-NEXT:          "character": 8,
> > +# CHECK-NEXT:          "line": 0
> > +# CHECK-NEXT:        },
> > +# CHECK-NEXT:        "start": {
> > +# CHECK-NEXT:          "character": 7,
> > +# CHECK-NEXT:          "line": 0
> > +# CHECK-NEXT:        }
> > +# CHECK-NEXT:      }
> > +# CHECK-NEXT:    },
> > +# CHECK-NEXT:    {
> > +# CHECK-NEXT:      "newText": "",
> > +# CHECK-NEXT:      "range": {
> > +# CHECK-NEXT:        "end": {
> > +# CHECK-NEXT:          "character": 10,
> > +# CHECK-NEXT:          "line": 0
> > +# CHECK-NEXT:        },
> > +# CHECK-NEXT:        "start": {
> > +# CHECK-NEXT:          "character": 9,
> > +# CHECK-NEXT:          "line": 0
> > +# CHECK-NEXT:        }
> > +# CHECK-NEXT:      }
> > +# CHECK-NEXT:    },
> > +# CHECK-NEXT:    {
> > +# CHECK-NEXT:      "newText": "",
> > +# CHECK-NEXT:      "range": {
> > +# CHECK-NEXT:        "end": {
> > +# CHECK-NEXT:          "character": 16,
> > +# CHECK-NEXT:          "line": 0
> > +# CHECK-NEXT:        },
> > +# CHECK-NEXT:        "start": {
> > +# CHECK-NEXT:          "character": 15,
> > +# CHECK-NEXT:          "line": 0
> > +# CHECK-NEXT:        }
> > +# CHECK-NEXT:      }
> > +# CHECK-NEXT:    }
> > +# CHECK-NEXT:  ]
> >
> >  Content-Length: 44
> >
> >  {"jsonrpc":"2.0","id":6,"method":"shutdown"}
> > -# CHECK: {"jsonrpc":"2.0","id":6,"result":null}
> >  Content-Length: 33
> >
> >  {"jsonrpc":"2.0":"method":"exit"}
> >
> > Modified: clang-tools-extra/trunk/test/clangd/initialize-params-invali
> d.test
> > URL: http://llvm.org/viewvc/llvm-project/clang-tools-
> > extra/trunk/test/clangd/initialize-params-
> > invalid.test?rev=317486&r1=317485&r2=317486&view=diff
> > ============================================================
> ==================
> > --- clang-tools-extra/trunk/test/clangd/initialize-params-invalid.test
> > (original)
> > +++ clang-tools-extra/trunk/test/clangd/initialize-params-invalid.test
> Mon Nov
> > 6 07:40:30 2017
> > @@ -1,27 +1,45 @@
> > -# RUN: clangd -run-synchronously < %s | FileCheck %s
> > +# RUN: clangd -pretty -run-synchronously < %s | FileCheck
> -strict-whitespace
> > %s
> >  # It is absolutely vital that this file has CRLF line endings.
> >  #
> >  # Test with invalid initialize request parameters
> >  Content-Length: 142
> >
> >
> > {"jsonrpc":"2.0","id":0,"method":"initialize","params":{"pro
> cessId":"","rootUr
> > i":"file:///path/to/workspace","capabilities":{},"trace":"off"}}
> > -# CHECK: Content-Length: 606
> > -# 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:   "signatureHelpProvider": {"triggerCharacters": ["(",","]},
> > -# CHECK:   "definitionProvider": true,
> > -# CHECK:   "executeCommandProvider": {"commands": ["clangd.applyFix"]}
> > -# CHECK: }}}
> > -#
> > +#      CHECK:  "id": 0,
> > +# CHECK-NEXT:  "jsonrpc": "2.0",
> > +# CHECK-NEXT:  "result": {
> > +# CHECK-NEXT:    "codeActionProvider": true,
> > +# CHECK-NEXT:    "completionProvider": {
> > +# CHECK-NEXT:      "resolveProvider": false,
> > +# CHECK-NEXT:      "triggerCharacters": [
> > +# CHECK-NEXT:        ".",
> > +# CHECK-NEXT:        ">",
> > +# CHECK-NEXT:        ":"
> > +# CHECK-NEXT:      ]
> > +# CHECK-NEXT:    },
> > +# CHECK-NEXT:    "definitionProvider": true,
> > +# CHECK-NEXT:    "documentFormattingProvider": true,
> > +# CHECK-NEXT:    "documentOnTypeFormattingProvider": {
> > +# CHECK-NEXT:      "firstTriggerCharacter": "}",
> > +# CHECK-NEXT:      "moreTriggerCharacter": []
> > +# CHECK-NEXT:    },
> > +# CHECK-NEXT:    "documentRangeFormattingProvider": true,
> > +# CHECK-NEXT:    "executeCommandProvider": {
> > +# CHECK-NEXT:      "commands": [
> > +# CHECK-NEXT:        "clangd.applyFix"
> > +# CHECK-NEXT:      ]
> > +# CHECK-NEXT:    },
> > +# CHECK-NEXT:    "signatureHelpProvider": {
> > +# CHECK-NEXT:      "triggerCharacters": [
> > +# CHECK-NEXT:        "(",
> > +# CHECK-NEXT:        ","
> > +# CHECK-NEXT:      ]
> > +# CHECK-NEXT:    },
> > +# CHECK-NEXT:    "textDocumentSync": 1
> > +# CHECK-NEXT:  }
> >  Content-Length: 44
> >
> >  {"jsonrpc":"2.0","id":3,"method":"shutdown"}
> > -# CHECK: {"jsonrpc":"2.0","id":3,"result":null}
> >  Content-Length: 33
> >
> >  {"jsonrpc":"2.0":"method":"exit"}
> >
> > Modified: clang-tools-extra/trunk/test/clangd/initialize-params.test
> > URL: http://llvm.org/viewvc/llvm-project/clang-tools-
> > extra/trunk/test/clangd/initialize-
> > params.test?rev=317486&r1=317485&r2=317486&view=diff
> > ============================================================
> ==================
> > --- clang-tools-extra/trunk/test/clangd/initialize-params.test
> (original)
> > +++ clang-tools-extra/trunk/test/clangd/initialize-params.test Mon Nov
> 6
> > 07:40:30 2017
> > @@ -1,27 +1,48 @@
> > -# RUN: clangd -run-synchronously < %s | FileCheck %s
> > +# RUN: clangd -pretty -run-synchronously < %s | FileCheck
> -strict-whitespace
> > %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":{"pro
> cessId":123,"rootU
> > ri":"file:///path/to/workspace","capabilities":{},"trace":"off"}}
> > -# CHECK: Content-Length: 606
> > -# 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:   "signatureHelpProvider": {"triggerCharacters": ["(",","]},
> > -# CHECK:   "definitionProvider": true,
> > -# CHECK:   "executeCommandProvider": {"commands": ["clangd.applyFix"]}
> > -# CHECK: }}}
> > -#
> > +#      CHECK:  "id": 0,
> > +# CHECK-NEXT:  "jsonrpc": "2.0",
> > +# CHECK-NEXT:  "result": {
> > +# CHECK-NEXT:    "codeActionProvider": true,
> > +# CHECK-NEXT:    "completionProvider": {
> > +# CHECK-NEXT:      "resolveProvider": false,
> > +# CHECK-NEXT:      "triggerCharacters": [
> > +# CHECK-NEXT:        ".",
> > +# CHECK-NEXT:        ">",
> > +# CHECK-NEXT:        ":"
> > +# CHECK-NEXT:      ]
> > +# CHECK-NEXT:    },
> > +# CHECK-NEXT:    "definitionProvider": true,
> > +# CHECK-NEXT:    "documentFormattingProvider": true,
> > +# CHECK-NEXT:    "documentOnTypeFormattingProvider": {
> > +# CHECK-NEXT:      "firstTriggerCharacter": "}",
> > +# CHECK-NEXT:      "moreTriggerCharacter": []
> > +# CHECK-NEXT:    },
> > +# CHECK-NEXT:    "documentRangeFormattingProvider": true,
> > +# CHECK-NEXT:    "executeCommandProvider": {
> > +# CHECK-NEXT:      "commands": [
> > +# CHECK-NEXT:        "clangd.applyFix"
> > +# CHECK-NEXT:      ]
> > +# CHECK-NEXT:    },
> > +# CHECK-NEXT:    "signatureHelpProvider": {
> > +# CHECK-NEXT:      "triggerCharacters": [
> > +# CHECK-NEXT:        "(",
> > +# CHECK-NEXT:        ","
> > +# CHECK-NEXT:      ]
> > +# CHECK-NEXT:    },
> > +# CHECK-NEXT:    "textDocumentSync": 1
> > +# CHECK-NEXT:  }
> >  Content-Length: 44
> >
> >  {"jsonrpc":"2.0","id":3,"method":"shutdown"}
> > -# CHECK: {"jsonrpc":"2.0","id":3,"result":null}
> > +#      CHECK:  "id": 3,
> > +# CHECK-NEXT:  "jsonrpc": "2.0",
> > +# CHECK-NEXT:  "result": null
> >  Content-Length: 33
> >
> >  {"jsonrpc":"2.0":"method":"exit"}
> >
> > Modified: clang-tools-extra/trunk/test/clangd/input-mirror.test
> > URL: http://llvm.org/viewvc/llvm-project/clang-tools-
> > extra/trunk/test/clangd/input-
> > mirror.test?rev=317486&r1=317485&r2=317486&view=diff
> > ============================================================
> ==================
> > --- clang-tools-extra/trunk/test/clangd/input-mirror.test (original)
> > +++ clang-tools-extra/trunk/test/clangd/input-mirror.test Mon Nov  6
> 07:40:30
> > 2017
> > @@ -1,4 +1,4 @@
> > -# RUN: clangd -run-synchronously -input-mirror-file %t < %s
> > +# RUN: clangd -pretty -run-synchronously -input-mirror-file %t < %s
> >  # Note that we have to use '-b' as -input-mirror-file does not have a
> newline
> > at the end of file.
> >  # RUN: diff -b %t %s
> >  # It is absolutely vital that this file has CRLF line endings.
> > @@ -152,7 +152,6 @@ Content-Length: 148
> >  Content-Length: 44
> >
> >  {"jsonrpc":"2.0","id":3,"method":"shutdown"}
> > -# CHECK: {"jsonrpc":"2.0","id":3,"result":null}
> >  Content-Length: 33
> >
> >  {"jsonrpc":"2.0":"method":"exit"}
> >
> > Modified: clang-tools-extra/trunk/test/clangd/protocol.test
> > URL: http://llvm.org/viewvc/llvm-project/clang-tools-
> > extra/trunk/test/clangd/protocol.test?rev=317486&r1=317485&r
> 2=317486&view=diff
> > ============================================================
> ==================
> > --- clang-tools-extra/trunk/test/clangd/protocol.test (original)
> > +++ clang-tools-extra/trunk/test/clangd/protocol.test Mon Nov  6
> 07:40:30 2017
> > @@ -1,5 +1,5 @@
> > -# RUN: not clangd -run-synchronously < %s | FileCheck %s
> > -# RUN: not clangd -run-synchronously < %s 2>&1 | FileCheck -check-
> > prefix=STDERR %s
> > +# RUN: not clangd -pretty -run-synchronously < %s | FileCheck -strict-
> > whitespace %s
> > +# RUN: not clangd -pretty -run-synchronously < %s 2>&1 | FileCheck
> -check-
> > prefix=STDERR %s
> >  # vim: fileformat=dos
> >  # It is absolutely vital that this file has CRLF line endings.
> >  #
> > @@ -12,16 +12,9 @@ Content-Type: application/vscode-jsonrpc
> >
> > {"jsonrpc":"2.0","id":0,"method":"initialize","params":{"pro
> cessId":123,"rootP
> > ath":"clangd","capabilities":{},"trace":"off"}}
> >  # Test message with Content-Type after Content-Length
> >  #
> > -# CHECK: "jsonrpc":"2.0","id":0,"result":{"capabilities":{
> > -# CHECK-DAG: "textDocumentSync": 1,
> > -# CHECK-DAG: "documentFormattingProvider": true,
> > -# CHECK-DAG: "documentRangeFormattingProvider": true,
> > -# CHECK-DAG: "documentOnTypeFormattingProvider":
> > {"firstTriggerCharacter":"}","moreTriggerCharacter":[]},
> > -# CHECK-DAG: "codeActionProvider": true,
> > -# CHECK-DAG: "completionProvider": {"resolveProvider": false,
> > "triggerCharacters": [".",">",":"]},
> > -# CHECK-DAG: "definitionProvider": true
> > -# CHECK: }}
> > -
> > +#      CHECK:  "jsonrpc": "2.0",
> > +# CHECK-NEXT:  "result": {
> > +#      CHECK:  }
> >  Content-Length: 246
> >
> >
> > {"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"
> textDocument":{"ur
> > i":"file:///main.cpp","languageId":"cpp","version":1,"text":"struct
> fake { int
> > a, bb, ccc; int f(int i, const float f) const; };\nint main() {\n  fake
> f;\n
> > f.\n}\n"}}}
> > @@ -36,9 +29,16 @@ Content-Length: 146
> >
> > {"jsonrpc":"2.0","id":1,"method":"textDocument/completion","
> params":{"textDocu
> > ment":{"uri":"file:/main.cpp"},"position":{"line":3,"character":5}}}
> >  # Test message with Content-Type before Content-Length
> >  #
> > -# CHECK: {"jsonrpc":"2.0","id":1,"result":[
> > -# CHECK-DAG:
> > {"label":"a","kind":5,"detail":"int","sortText":"000035a","f
> ilterText":"a","in
> > sertText":"a","insertTextFormat":1}
> > -# CHECK: ]}
> > +#      CHECK:  "id": 1,
> > +# CHECK-NEXT:  "jsonrpc": "2.0",
> > +# CHECK-NEXT:  "result": [
> > +#      CHECK:      "filterText": "fake",
> > +# CHECK-NEXT:      "insertText": "fake",
> > +# CHECK-NEXT:      "insertTextFormat": 1,
> > +# CHECK-NEXT:      "kind": 7,
> > +# CHECK-NEXT:      "label": "fake::",
> > +# CHECK-NEXT:      "sortText": "000075fake"
> > +#      CHECK:  ]
> >
> >  X-Test: Testing
> >  Content-Type: application/vscode-jsonrpc; charset-utf-8
> > @@ -55,9 +55,16 @@ Content-Length: 146
> >
> > {"jsonrpc":"2.0","id":3,"method":"textDocument/completion","
> params":{"textDocu
> > ment":{"uri":"file:/main.cpp"},"position":{"line":3,"character":5}}}
> >  # Test message with duplicate Content-Length headers
> >  #
> > -# CHECK: {"jsonrpc":"2.0","id":3,"result":[
> > -# CHECK-DAG:
> > {"label":"a","kind":5,"detail":"int","sortText":"000035a","f
> ilterText":"a","in
> > sertText":"a","insertTextFormat":1}
> > -# CHECK: ]}
> > +#      CHECK:  "id": 3,
> > +# CHECK-NEXT:  "jsonrpc": "2.0",
> > +# CHECK-NEXT:  "result": [
> > +#      CHECK:      "filterText": "fake",
> > +# CHECK-NEXT:      "insertText": "fake",
> > +# CHECK-NEXT:      "insertTextFormat": 1,
> > +# CHECK-NEXT:      "kind": 7,
> > +# CHECK-NEXT:      "label": "fake::",
> > +# CHECK-NEXT:      "sortText": "000075fake"
> > +#      CHECK:  ]
> >  # STDERR: Warning: Duplicate Content-Length header received. The
> previous
> > value for this message (10) was ignored.
> >
> >  Content-Type: application/vscode-jsonrpc; charset-utf-8
> > @@ -74,10 +81,16 @@ Content-Length: 146
> >
> > {"jsonrpc":"2.0","id":5,"method":"textDocument/completion","
> params":{"textDocu
> > ment":{"uri":"file:/main.cpp"},"position":{"line":3,"character":5}}}
> >  # Test message with Content-Type before Content-Length
> >  #
> > -# CHECK: {"jsonrpc":"2.0","id":5,"result":[
> > -# CHECK-DAG:
> > {"label":"a","kind":5,"detail":"int","sortText":"000035a","f
> ilterText":"a","in
> > sertText":"a","insertTextFormat":1}
> > -# CHECK: ]}
> > -
> > +#      CHECK:  "id": 5,
> > +# CHECK-NEXT:  "jsonrpc": "2.0",
> > +# CHECK-NEXT:  "result": [
> > +#      CHECK:      "filterText": "fake",
> > +# CHECK-NEXT:      "insertText": "fake",
> > +# CHECK-NEXT:      "insertTextFormat": 1,
> > +# CHECK-NEXT:      "kind": 7,
> > +# CHECK-NEXT:      "label": "fake::",
> > +# CHECK-NEXT:      "sortText": "000075fake"
> > +#      CHECK:  ]
> >  Content-Length: 1024
> >
> >
> > {"jsonrpc":"2.0","id":5,"method":"textDocument/completion","
> params":{"textDocu
> > ment":{"uri":"file:/main.cpp"},"position":{"line":3,"character":5}}}
> >
> > Modified: clang-tools-extra/trunk/test/clangd/signature-help.test
> > URL: http://llvm.org/viewvc/llvm-project/clang-tools-
> > extra/trunk/test/clangd/signature-
> > help.test?rev=317486&r1=317485&r2=317486&view=diff
> > ============================================================
> ==================
> > --- clang-tools-extra/trunk/test/clangd/signature-help.test (original)
> > +++ clang-tools-extra/trunk/test/clangd/signature-help.test Mon Nov  6
> > 07:40:30 2017
> > @@ -15,12 +15,12 @@ Content-Length: 333
> >  Content-Length: 151
> >
> >
> > {"jsonrpc":"2.0","id":1,"method":"textDocument/signatureHelp
> ","params":{"textD
> > ocument":{"uri":"file:///main.cpp"},"position":{"line":8,"ch
> aracter":9}}}
> > -# CHECK:
> > {"jsonrpc":"2.0","id":1,"result":{"activeSignature":0,"activ
> eParameter":0,"sig
> > natures":[
> > +# CHECK:
> > {"id":1,"jsonrpc":"2.0","result":{"activeParameter":0,"activ
> eSignature":0,"sig
> > natures":[
> >  # CHECK-DAG: {"label":"foo(float x, float y) ->
> > void","parameters":[{"label":"float x"},{"label":"float y"}]}
> >  # CHECK-DAG: {"label":"foo(float x, int y) ->
> > void","parameters":[{"label":"float x"},{"label":"int y"}]}
> >  # CHECK-DAG: {"label":"foo(int x, float y) ->
> > void","parameters":[{"label":"int x"},{"label":"float y"}]}
> >  # CHECK-DAG: {"label":"foo(int x, int y) ->
> void","parameters":[{"label":"int
> > x"},{"label":"int y"}]}
> > -# CHECK: ]}
> > +# CHECK-SAME: ]}
> >
> >  # Modify the document
> >  Content-Length: 333
> > @@ -31,21 +31,20 @@ Content-Length: 333
> >  Content-Length: 151
> >
> >
> > {"jsonrpc":"2.0","id":2,"method":"textDocument/signatureHelp
> ","params":{"textD
> > ocument":{"uri":"file:///main.cpp"},"position":{"line":8,"ch
> aracter":9}}}
> > -# CHECK:
> > {"jsonrpc":"2.0","id":2,"result":{"activeSignature":0,"activ
> eParameter":0,"sig
> > natures":[
> > +# CHECK:
> > {"id":2,"jsonrpc":"2.0","result":{"activeParameter":0,"activ
> eSignature":0,"sig
> > natures":[
> >  # CHECK-DAG: {"label":"bar(int x, int y = 0) ->
> > void","parameters":[{"label":"int x"},{"label":"int y = 0"}]}
> >  # CHECK-DAG: {"label":"bar(float x = 0, int y = 42) ->
> > void","parameters":[{"label":"float x = 0"},{"label":"int y = 42"}]}
> > -# CHECK: ]}
> > +# CHECK-SAME: ]}
> >
> >  Content-Length: 159
> >
> >
> > {"jsonrpc":"2.0","id":3,"method":"textDocument/signatureHelp
> ","params":{"textD
> > ocument":{"uri":"file:///doesnotexist.cpp"},"position":{"lin
> e":8,"character":9
> > }}}
> > -# CHECK: {"jsonrpc":"2.0","id":3,"error":{"code":-
> > 32602,"message":"signatureHelp is called for non-added document"}}
> > +# CHECK: {"error":{"code":-32602,"message":"signatureHelp is called
> for non-
> > added document"},"id":3,"jsonrpc":"2.0"}
> >
> >  # Shutdown.
> >  Content-Length: 49
> >
> >  {"jsonrpc":"2.0","id":100000,"method":"shutdown"}
> > -# CHECK: {"jsonrpc":"2.0","id":100000,"result":null}
> >  Content-Length: 33
> >
> >  {"jsonrpc":"2.0":"method":"exit"}
> >
> > Modified: clang-tools-extra/trunk/test/clangd/unsupported-method.test
> > URL: http://llvm.org/viewvc/llvm-project/clang-tools-
> > extra/trunk/test/clangd/unsupported-
> > method.test?rev=317486&r1=317485&r2=317486&view=diff
> > ============================================================
> ==================
> > --- clang-tools-extra/trunk/test/clangd/unsupported-method.test
> (original)
> > +++ clang-tools-extra/trunk/test/clangd/unsupported-method.test Mon
> Nov  6
> > 07:40:30 2017
> > @@ -1,4 +1,4 @@
> > -# RUN: clangd -run-synchronously < %s | FileCheck %s
> > +# RUN: clangd -pretty -run-synchronously < %s | FileCheck
> -strict-whitespace
> > %s
> >  # It is absolutely vital that this file has CRLF line endings.
> >  #
> >  Content-Length: 125
> > @@ -12,12 +12,16 @@ Content-Length: 143
> >  Content-Length: 92
> >
> >
> > {"jsonrpc":"2.0","id":1,"method":"textDocument/jumpInTheAirL
> ikeYouJustDontCare
> > ","params":{}}
> > -# CHECK: {"jsonrpc":"2.0","id":1,"error":{"code":-32601,"message":"method
> not
> > found"}}
> > +#      CHECK:  "error": {
> > +# CHECK-NEXT:    "code": -32601,
> > +# CHECK-NEXT:    "message": "method not found"
> > +# CHECK-NEXT:  },
> > +# CHECK-NEXT:  "id": 1,
> > +# CHECK-NEXT:  "jsonrpc": "2.0"
> >
> >  Content-Length: 44
> >
> >  {"jsonrpc":"2.0","id":2,"method":"shutdown"}
> > -# CHECK: {"jsonrpc":"2.0","id":2,"result":null}
> >  Content-Length: 33
> >
> >  {"jsonrpc":"2.0":"method":"exit"}
> >
> > Modified: clang-tools-extra/trunk/unittests/clangd/CMakeLists.txt
> > URL: http://llvm.org/viewvc/llvm-project/clang-tools-
> > extra/trunk/unittests/clangd/CMakeLists.txt?rev=317486&r1=31
> 7485&r2=317486&vie
> > w=diff
> > ============================================================
> ==================
> > --- clang-tools-extra/trunk/unittests/clangd/CMakeLists.txt (original)
> > +++ clang-tools-extra/trunk/unittests/clangd/CMakeLists.txt Mon Nov  6
> > 07:40:30 2017
> > @@ -10,6 +10,7 @@ include_directories(
> >
> >  add_extra_unittest(ClangdTests
> >    ClangdTests.cpp
> > +  JSONExprTests.cpp
> >    TraceTests.cpp
> >    )
> >
> >
> > Added: clang-tools-extra/trunk/unittests/clangd/JSONExprTests.cpp
> > URL: http://llvm.org/viewvc/llvm-project/clang-tools-
> > extra/trunk/unittests/clangd/JSONExprTests.cpp?rev=317486&view=auto
> > ============================================================
> ==================
> > --- clang-tools-extra/trunk/unittests/clangd/JSONExprTests.cpp (added)
> > +++ clang-tools-extra/trunk/unittests/clangd/JSONExprTests.cpp Mon Nov
> 6
> > 07:40:30 2017
> > @@ -0,0 +1,112 @@
> > +//===-- JSONExprTests.cpp - JSON expression unit tests ----------*- C++
> -*-
> > ===//
> > +//
> > +//                     The LLVM Compiler Infrastructure
> > +//
> > +// This file is distributed under the University of Illinois Open Source
> > +// License. See LICENSE.TXT for details.
> > +//
> > +//===------------------------------------------------------
> ----------------
> > ===//
> > +
> > +#include "JSONExpr.h"
> > +
> > +#include "gmock/gmock.h"
> > +#include "gtest/gtest.h"
> > +
> > +namespace clang {
> > +namespace clangd {
> > +namespace json {
> > +namespace {
> > +
> > +std::string s(const Expr &E) { return llvm::formatv("{0}", E).str(); }
> > +std::string sp(const Expr &E) { return llvm::formatv("{0:2}", E).str();
> }
> > +
> > +TEST(JSONExprTests, Types) {
> > +  EXPECT_EQ("true", s(true));
> > +  EXPECT_EQ("null", s(nullptr));
> > +  EXPECT_EQ("2.5", s(2.5));
> > +  EXPECT_EQ(R"("foo")", s("foo"));
> > +  EXPECT_EQ("[1,2,3]", s({1, 2, 3}));
> > +  EXPECT_EQ(R"({"x":10,"y":20})", s(obj{{"x", 10}, {"y", 20}}));
> > +}
> > +
> > +TEST(JSONExprTests, Constructors) {
> > +  // Lots of edge cases around empty and singleton init lists.
> > +  EXPECT_EQ("[[[3]]]", s({{{3}}}));
> > +  EXPECT_EQ("[[[]]]", s({{{}}}));
> > +  EXPECT_EQ("[[{}]]", s({{obj{}}}));
> > +  EXPECT_EQ(R"({"A":{"B":{}}})", s(obj{{"A", obj{{"B", obj{}}}}}));
> > +  EXPECT_EQ(R"({"A":{"B":{"X":"Y"}}})",
> > +            s(obj{{"A", obj{{"B", obj{{"X", "Y"}}}}}}));
> > +}
> > +
> > +TEST(JSONExprTests, StringOwnership) {
> > +  char X[] = "Hello";
> > +  Expr Alias = static_cast<const char *>(X);
> > +  X[1] = 'a';
> > +  EXPECT_EQ(R"("Hallo")", s(Alias));
> > +
> > +  std::string Y = "Hello";
> > +  Expr Copy = Y;
> > +  Y[1] = 'a';
> > +  EXPECT_EQ(R"("Hello")", s(Copy));
> > +}
> > +
> > +TEST(JSONExprTests, CanonicalOutput) {
> > +  // Objects are sorted (but arrays aren't)!
> > +  EXPECT_EQ(R"({"a":1,"b":2,"c":3})", s(obj{{"a", 1}, {"c", 3}, {"b",
> 2}}));
> > +  EXPECT_EQ(R"(["a","c","b"])", s({"a", "c", "b"}));
> > +  EXPECT_EQ("3", s(3.0));
> > +}
> > +
> > +TEST(JSONExprTests, Escaping) {
> > +  std::string test = {
> > +      0,                    // Strings may contain nulls.
> > +      '\b',   '\f',         // Have mnemonics, but we escape
> numerically.
> > +      '\r',   '\n',   '\t', // Escaped with mnemonics.
> > +      'S',    '\"',   '\\', // Printable ASCII characters.
> > +      '\x7f',               // Delete is not escaped.
> > +      '\xce', '\x94',       // Non-ASCII UTF-8 is not escaped.
> > +  };
> > +  EXPECT_EQ(R"("\u0000\u0008\u000c\r\n\tS\"\\)"
> > +            u8"\x7fΔ\"",
> > +            s(test));
> > +
> > +  EXPECT_EQ(R"({"object keys are\nescaped":true})",
> > +            s(obj{{"object keys are\nescaped", true}}));
> > +}
> > +
> > +TEST(JSONExprTests, PrettyPrinting) {
> > +  EXPECT_EQ(
> > +      R"({
> > +  "empty_array": [],
> > +  "empty_object": {},
> > +  "full_array": [
> > +    1,
> > +    null
> > +  ],
> > +  "full_object": {
> > +    "nested_array": [
> > +      {
> > +        "property": "value"
> > +      }
> > +    ]
> > +  }
> > +})",
> > +      sp(obj{
> > +          {"empty_object", obj{}},
> > +          {"empty_array", {}},
> > +          {"full_array", {1, nullptr}},
> > +          {"full_object",
> > +           obj{
> > +               {"nested_array",
> > +                {obj{
> > +                    {"property", "value"},
> > +                }}},
> > +           }},
> > +      }));
> > +}
> > +
> > +} // namespace
> > +} // namespace json
> > +} // namespace clangd
> > +} // namespace clang
> >
> >
> > _______________________________________________
> > cfe-commits mailing list
> > cfe-commits at lists.llvm.org
> > http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
>
>
>
>
>
> --
> Regards,
> Ilya Biryukov
>
>
>
>
>
>
> --
> Regards,
> Ilya Biryukov
>



-- 
Regards,
Ilya Biryukov
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-commits/attachments/20171108/f8448356/attachment-0001.html>


More information about the cfe-commits mailing list