//===--- Requests.cpp -----------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

#include "DictionaryKeys.h"
#include "sourcekitd/CodeCompletionResultsArray.h"
#include "sourcekitd/DocSupportAnnotationArray.h"
#include "sourcekitd/TokenAnnotationsArray.h"

#include "SourceKit/Core/Context.h"
#include "SourceKit/Core/LangSupport.h"
#include "SourceKit/Core/NotificationCenter.h"
#include "SourceKit/Support/Concurrency.h"
#include "SourceKit/Support/Logging.h"
#include "SourceKit/Support/UIdent.h"

#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/raw_ostream.h"
#include <mutex>

// FIXME: Portability.
#include <dispatch/dispatch.h>

using namespace sourcekitd;
using namespace SourceKit;

namespace {
class LazySKDUID {
  const char *Name;
  mutable std::atomic<sourcekitd_uid_t> UID { nullptr };
public:
  LazySKDUID(const char *Name) : Name(Name) { }

  sourcekitd_uid_t get() const {
    if (!UID)
      UID = SKDUIDFromUIdent(UIdent(Name));
    return UID;
  }

  operator sourcekitd_uid_t() const {
    return get();
  }
};
} // anonymous namespace.

static LazySKDUID RequestIndex("source.request.indexsource");
static LazySKDUID RequestDocInfo("source.request.docinfo");
static LazySKDUID RequestCodeComplete("source.request.codecomplete");
static LazySKDUID RequestCodeCompleteOpen("source.request.codecomplete.open");
static LazySKDUID RequestCodeCompleteClose("source.request.codecomplete.close");
static LazySKDUID
    RequestCodeCompleteUpdate("source.request.codecomplete.update");
static LazySKDUID
    RequestCodeCompleteCacheOnDisk("source.request.codecomplete.cache.ondisk");
static LazySKDUID RequestCodeCompleteSetPopularAPI(
    "source.request.codecomplete.setpopularapi");
static LazySKDUID
    RequestCodeCompleteSetCustom("source.request.codecomplete.setcustom");
static LazySKDUID RequestCursorInfo("source.request.cursorinfo");
static LazySKDUID RequestRelatedIdents("source.request.relatedidents");
static LazySKDUID RequestEditorOpen("source.request.editor.open");
static LazySKDUID RequestEditorOpenInterface(
    "source.request.editor.open.interface");
static LazySKDUID RequestEditorOpenHeaderInterface(
    "source.request.editor.open.interface.header");
static LazySKDUID RequestEditorOpenSwiftSourceInterface(
    "source.request.editor.open.interface.swiftsource");
static LazySKDUID RequestEditorExtractTextFromComment(
    "source.request.editor.extract.comment");
static LazySKDUID RequestEditorClose("source.request.editor.close");
static LazySKDUID RequestEditorReplaceText("source.request.editor.replacetext");
static LazySKDUID RequestEditorFormatText("source.request.editor.formattext");
static LazySKDUID RequestEditorExpandPlaceholder(
    "source.request.editor.expand_placeholder");
static LazySKDUID RequestEditorFindUSR("source.request.editor.find_usr");
static LazySKDUID RequestEditorFindInterfaceDoc(
    "source.request.editor.find_interface_doc");
static LazySKDUID RequestBuildSettingsRegister(
    "source.request.buildsettings.register");

static LazySKDUID KindExpr("source.lang.swift.expr");
static LazySKDUID KindStmt("source.lang.swift.stmt");
static LazySKDUID KindType("source.lang.swift.type");

static UIdent DiagKindNote("source.diagnostic.severity.note");
static UIdent DiagKindWarning("source.diagnostic.severity.warning");
static UIdent DiagKindError("source.diagnostic.severity.error");

static void onDocumentUpdateNotification(StringRef DocumentName) {
  static UIdent DocumentUpdateNotificationUID(
      "source.notification.editor.documentupdate");

  ResponseBuilder RespBuilder;
  auto Dict = RespBuilder.getDictionary();
  Dict.set(KeyNotification, DocumentUpdateNotificationUID);
  Dict.set(KeyName, DocumentName);
  
  sourcekitd::postNotification(RespBuilder.createResponse());
}

static SourceKit::Context *GlobalCtx = nullptr;

void sourcekitd::initialize() {
  GlobalCtx = new SourceKit::Context(sourcekitd::getRuntimeLibPath());
  GlobalCtx->getNotificationCenter().addDocumentUpdateNotificationReceiver(
    onDocumentUpdateNotification);
}
void sourcekitd::shutdown() {
  delete GlobalCtx;
  GlobalCtx = nullptr;
}

static SourceKit::Context &getGlobalContext() {
  assert(GlobalCtx);
  return *GlobalCtx;
}

static sourcekitd_response_t indexSource(StringRef Filename,
                                         ArrayRef<const char *> Args,
                                         StringRef KnownHash);

static sourcekitd_response_t reportDocInfo(llvm::MemoryBuffer *InputBuf,
                                           StringRef ModuleName,
                                           ArrayRef<const char *> Args);

static void reportCursorInfo(StringRef Filename,
                             int64_t Offset,
                             ArrayRef<const char *> Args,
                             ResponseReceiver Rec);

static void findRelatedIdents(StringRef Filename,
                              int64_t Offset,
                              ArrayRef<const char *> Args,
                              ResponseReceiver Rec);

static sourcekitd_response_t codeComplete(llvm::MemoryBuffer *InputBuf,
                                          int64_t Offset,
                                          ArrayRef<const char *> Args);

static sourcekitd_response_t codeCompleteOpen(StringRef name,
                                              llvm::MemoryBuffer *InputBuf,
                                              int64_t Offset,
                                              Optional<RequestDict> optionsDict,
                                              ArrayRef<const char *> Args);

static sourcekitd_response_t
codeCompleteUpdate(StringRef name, int64_t Offset,
                   Optional<RequestDict> optionsDict);

static sourcekitd_response_t codeCompleteClose(StringRef name, int64_t Offset);

static sourcekitd_response_t
editorOpen(StringRef Name, llvm::MemoryBuffer *Buf, bool EnableSyntaxMap,
           bool EnableStructure, bool EnableDiagnostics, bool SyntacticOnly,
           ArrayRef<const char *> Args);

static sourcekitd_response_t
editorOpenInterface(StringRef Name, StringRef ModuleName,
                    ArrayRef<const char *> Args);

static sourcekitd_response_t
editorOpenHeaderInterface(StringRef Name, StringRef HeaderName,
                          ArrayRef<const char *> Args);

static void
editorOpenSwiftSourceInterface(StringRef Name, StringRef SourceName,
                               ArrayRef<const char *> Args,
                               ResponseReceiver Rec);

static sourcekitd_response_t editorExtractTextFromComment(StringRef Source);

static sourcekitd_response_t
editorClose(StringRef Name, bool RemoveCache);

static sourcekitd_response_t
editorReplaceText(StringRef Name, llvm::MemoryBuffer *Buf, unsigned Offset,
                  unsigned Length, bool EnableSyntaxMap, bool EnableStructure,
                  bool EnableDiagnostics, bool SyntacticOnly);

static void
editorApplyFormatOptions(StringRef Name, RequestDict &FmtOptions);

static sourcekitd_response_t
editorFormatText(StringRef Name, unsigned Line, unsigned Length);

static sourcekitd_response_t
editorExpandPlaceholder(StringRef Name, unsigned Offset, unsigned Length);

static sourcekitd_response_t
editorFindUSR(StringRef DocumentName, StringRef USR);

static sourcekitd_response_t
editorFindInterfaceDoc(StringRef ModuleName, ArrayRef<const char *> Args);

static bool isSemanticEditorDisabled();

static void fillDictionaryForDiagnosticInfo(
    ResponseBuilder::Dictionary Elem, const DiagnosticEntryInfoBase &Info);

static void handleRequestImpl(sourcekitd_object_t Req,
                              ResponseReceiver Receiver);

void sourcekitd::handleRequest(sourcekitd_object_t Req,
                               ResponseReceiver Receiver) {
  LOG_SECTION("handleRequest-before", InfoHighPrio) {
    sourcekitd::printRequestObject(Req, Log->getOS());
  }

  handleRequestImpl(Req, [Receiver](sourcekitd_response_t Resp) {
    LOG_SECTION("handleRequest-after", InfoHighPrio) {
      // Responses are big, print them out with info medium priority.
      if (Logger::isLoggingEnabledForLevel(Logger::Level::InfoMediumPrio))
        sourcekitd::printResponse(Resp, Log->getOS());
    }

    Receiver(Resp);
  });
}

static std::unique_ptr<llvm::MemoryBuffer>
getInputBufForRequest(Optional<StringRef> SourceFile,
                      Optional<StringRef> SourceText,
                      llvm::SmallString<64> &ErrBuf) {

  std::unique_ptr<llvm::MemoryBuffer> InputBuf;

  if (SourceText.hasValue()) {
    StringRef BufName;
    if (SourceFile.hasValue())
      BufName = *SourceFile;
    else
      BufName = "<input>";
    InputBuf = llvm::MemoryBuffer::getMemBuffer(*SourceText, BufName);

  } else if (SourceFile.hasValue()) {
    llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> FileBufOrErr =
      llvm::MemoryBuffer::getFile(*SourceFile);
    if (FileBufOrErr) {
      InputBuf = std::move(FileBufOrErr.get());
    } else {
      llvm::raw_svector_ostream OSErr(ErrBuf);
      OSErr << "error opening input file '" << *SourceFile << "' ("
            << FileBufOrErr.getError().message() << ')';
      return nullptr;
    }
  } else {
    InputBuf = llvm::MemoryBuffer::getNewMemBuffer(0, "<input>");
  }

  return InputBuf;
}


static void
handleSemanticRequest(RequestDict Req,
                      ResponseReceiver Receiver,
                      sourcekitd_uid_t ReqUID,
                      Optional<StringRef> SourceFile,
                      Optional<StringRef> SourceText,
                      ArrayRef<const char *> Args);

void handleRequestImpl(sourcekitd_object_t ReqObj, ResponseReceiver Rec) {
  RequestDict Req(ReqObj);
  sourcekitd_uid_t ReqUID = Req.getUID(KeyRequest);
  if (!ReqUID)
    return Rec(createErrorRequestInvalid("missing 'key.request' with UID value"));

  // Just accept 'source.request.buildsettings.register' for now, don't do
  // anything else.
  // FIXME: Heavy WIP here.
  if (ReqUID == RequestBuildSettingsRegister) {
    return Rec(ResponseBuilder().createResponse());
  }

  Optional<StringRef> SourceFile = Req.getString(KeySourceFile);
  Optional<StringRef> SourceText = Req.getString(KeySourceText);

  llvm::SmallString<64> ErrBuf;

  SmallVector<const char *, 8> Args;
  bool Failed = Req.getStringArray(KeyCompilerArgs, Args, /*isOptional=*/true);
  if (Failed) {
    return Rec(createErrorRequestInvalid(
        "'key.compilerargs' not an array of strings"));
  }

  if (ReqUID == RequestDocInfo) {
    std::unique_ptr<llvm::MemoryBuffer>
      InputBuf = getInputBufForRequest(SourceFile, SourceText, ErrBuf);
    if (!InputBuf)
      return Rec(createErrorRequestFailed(ErrBuf.c_str()));
    StringRef ModuleName;
    Optional<StringRef> ModuleNameOpt = Req.getString(KeyModuleName);
    if (ModuleNameOpt.hasValue()) ModuleName = *ModuleNameOpt;
    return Rec(reportDocInfo(InputBuf.get(), ModuleName, Args));
  }

  if (ReqUID == RequestEditorOpen) {
    Optional<StringRef> Name = Req.getString(KeyName);
    if (!Name.hasValue())
      return Rec(createErrorRequestInvalid("missing 'key.name'"));
    std::unique_ptr<llvm::MemoryBuffer>
    InputBuf = getInputBufForRequest(SourceFile, SourceText, ErrBuf);
    if (!InputBuf)
      return Rec(createErrorRequestFailed(ErrBuf.c_str()));
    int64_t EnableSyntaxMap = true;
    Req.getInt64(KeyEnableSyntaxMap, EnableSyntaxMap, /*isOptional=*/true);
    int64_t EnableStructure = true;
    Req.getInt64(KeyEnableStructure, EnableStructure, /*isOptional=*/true);
    int64_t EnableDiagnostics = true;
    Req.getInt64(KeyEnableDiagnostics, EnableDiagnostics, /*isOptional=*/true);
    int64_t SyntacticOnly = false;
    Req.getInt64(KeySyntacticOnly, SyntacticOnly, /*isOptional=*/true);
    return Rec(editorOpen(*Name, InputBuf.get(), EnableSyntaxMap, EnableStructure,
                          EnableDiagnostics, SyntacticOnly, Args));
  }
  if (ReqUID == RequestEditorClose) {
    Optional<StringRef> Name = Req.getString(KeyName);
    if (!Name.hasValue())
      return Rec(createErrorRequestInvalid("missing 'key.name'"));

    // Whether we remove the cached AST from libcache, by default, false.
    int64_t RemoveCache = false;
    Req.getInt64(KeyRemoveCache, RemoveCache, /*isOptional=*/true);
    return Rec(editorClose(*Name, RemoveCache));
  }
  if (ReqUID == RequestEditorReplaceText) {
    Optional<StringRef> Name = Req.getString(KeyName);
    if (!Name.hasValue())
      return Rec(createErrorRequestInvalid("missing 'key.name'"));
    std::unique_ptr<llvm::MemoryBuffer>
    InputBuf = getInputBufForRequest(SourceFile, SourceText, ErrBuf);
    if (!InputBuf)
      return Rec(createErrorRequestFailed(ErrBuf.c_str()));
    int64_t Offset = 0;
    Req.getInt64(KeyOffset, Offset, /*isOptional=*/true);
    int64_t Length = 0;
    Req.getInt64(KeyLength, Length, /*isOptional=*/true);
    int64_t EnableSyntaxMap = true;
    Req.getInt64(KeyEnableSyntaxMap, EnableSyntaxMap, /*isOptional=*/true);
    int64_t EnableStructure = true;
    Req.getInt64(KeyEnableStructure, EnableStructure, /*isOptional=*/true);
    int64_t EnableDiagnostics = true;
    Req.getInt64(KeyEnableDiagnostics, EnableDiagnostics, /*isOptional=*/true);
    int64_t SyntacticOnly = false;
    Req.getInt64(KeySyntacticOnly, SyntacticOnly, /*isOptional=*/true);
    return Rec(editorReplaceText(*Name, InputBuf.get(), Offset, Length,
                                 EnableSyntaxMap, EnableStructure,
                                 EnableDiagnostics, SyntacticOnly));
  }
  if (ReqUID == RequestEditorFormatText) {
    Optional<StringRef> Name = Req.getString(KeyName);
    if (!Name.hasValue())
      return Rec(createErrorRequestInvalid("missing 'key.name'"));
    Optional<RequestDict> FmtOptions = Req.getDictionary(KeyFormatOptions);
    if (FmtOptions.hasValue())
      editorApplyFormatOptions(*Name, *FmtOptions);
    int64_t Line = 0;
    Req.getInt64(KeyLine, Line, /*isOptional=*/false);
    int64_t Length = 0;
    Req.getInt64(KeyLength, Length, /*isOptional=*/true);
    return Rec(editorFormatText(*Name, Line, Length));
  }
  if (ReqUID == RequestEditorExpandPlaceholder) {
    Optional<StringRef> Name = Req.getString(KeyName);
    if (!Name.hasValue())
      return Rec(createErrorRequestInvalid("missing 'key.name'"));
    int64_t Offset = 0;
    Req.getInt64(KeyOffset, Offset, /*isOptional=*/false);
    int64_t Length = 0;
    Req.getInt64(KeyLength, Length, /*isOptional=*/false);
    return Rec(editorExpandPlaceholder(*Name, Offset, Length));
  }

  if (ReqUID == RequestEditorOpenInterface) {
    Optional<StringRef> Name = Req.getString(KeyName);
    if (!Name.hasValue())
      return Rec(createErrorRequestInvalid("missing 'key.name'"));
    Optional<StringRef> ModuleName = Req.getString(KeyModuleName);
    if (!ModuleName.hasValue())
      return Rec(createErrorRequestInvalid("missing 'key.modulename'"));
    return Rec(editorOpenInterface(*Name, *ModuleName, Args));
  }

  if (ReqUID == RequestEditorOpenHeaderInterface) {
    Optional<StringRef> Name = Req.getString(KeyName);
    if (!Name.hasValue())
      return Rec(createErrorRequestInvalid("missing 'key.name'"));
    Optional<StringRef> HeaderName = Req.getString(KeyFilePath);
    if (!HeaderName.hasValue())
      return Rec(createErrorRequestInvalid("missing 'key.filepath'"));
    return Rec(editorOpenHeaderInterface(*Name, *HeaderName, Args));
  }

  if (ReqUID == RequestEditorOpenSwiftSourceInterface) {
    Optional<StringRef> Name = Req.getString(KeyName);
    if (!Name.hasValue())
      return Rec(createErrorRequestInvalid("missing 'key.name'"));
    Optional<StringRef> FileName = Req.getString(KeySourceFile);
    if (!FileName.hasValue())
      return Rec(createErrorRequestInvalid("missing 'key.sourcefile'"));
    return editorOpenSwiftSourceInterface(*Name, *FileName, Args, Rec);
  }

  if (ReqUID == RequestEditorExtractTextFromComment) {
    Optional<StringRef> Source = Req.getString(KeySourceText);
    if (!Source.hasValue())
      return Rec(createErrorRequestInvalid("missing 'key.sourcetext'"));
    return Rec(editorExtractTextFromComment(Source.getValue()));
  }

  if (ReqUID == RequestEditorFindUSR) {
    Optional<StringRef> Name = Req.getString(KeySourceFile);
    if (!Name.hasValue())
      return Rec(createErrorRequestInvalid("missing 'key.sourcefile'"));
    Optional<StringRef> USR = Req.getString(KeyUSR);
    if (!USR.hasValue())
      return Rec(createErrorRequestInvalid("missing 'key.usr'"));
    return Rec(editorFindUSR(*Name, *USR));
  }

  if (ReqUID == RequestEditorFindInterfaceDoc) {
    Optional<StringRef> ModuleName = Req.getString(KeyModuleName);
    if (!ModuleName.hasValue())
      return Rec(createErrorRequestInvalid("missing 'key.modulename'"));
    return Rec(editorFindInterfaceDoc(*ModuleName, Args));
  }

  if (ReqUID == RequestCodeCompleteClose) {
    // Unlike opening code completion, this is not a semantic request.
    Optional<StringRef> Name = Req.getString(KeyName);
    if (!Name.hasValue())
      return Rec(createErrorRequestInvalid("missing 'key.name'"));
    int64_t Offset;
    if (Req.getInt64(KeyOffset, Offset, /*isOptional=*/false))
      return Rec(createErrorRequestInvalid("missing 'key.offset'"));
    return Rec(codeCompleteClose(*Name, Offset));
  }

  if (ReqUID == RequestCodeCompleteCacheOnDisk) {
    Optional<StringRef> Name = Req.getString(KeyName);
    if (!Name.hasValue())
      return Rec(createErrorRequestInvalid("missing 'key.name'"));
    LangSupport &Lang = getGlobalContext().getSwiftLangSupport();
    Lang.codeCompleteCacheOnDisk(*Name);
    ResponseBuilder b;
    return Rec(b.createResponse());
  }

  if (ReqUID == RequestCodeCompleteSetPopularAPI) {
    llvm::SmallVector<const char *, 0> popular;
    llvm::SmallVector<const char *, 0> unpopular;
    Req.getStringArray(KeyPopular, popular, /*isOptional=*/false);
    Req.getStringArray(KeyUnpopular, unpopular, /*isOptional=*/false);
    LangSupport &Lang = getGlobalContext().getSwiftLangSupport();
    Lang.codeCompleteSetPopularAPI(popular, unpopular);
    ResponseBuilder b;
    return Rec(b.createResponse());
  }

  if (ReqUID == RequestCodeCompleteSetCustom) {
    SmallVector<CustomCompletionInfo, 16> customCompletions;
    sourcekitd_response_t err =
        createErrorRequestInvalid("missing 'key.results'");
    bool failed = Req.dictionaryArrayApply(KeyResults, [&](RequestDict dict) {
      CustomCompletionInfo CCInfo;
      Optional<StringRef> Name = dict.getString(KeyName);
      if (!Name.hasValue()) {
        err = createErrorRequestInvalid("missing 'key.name'");
        return true;
      }
      CCInfo.Name = *Name;

      sourcekitd_uid_t Kind = dict.getUID(KeyKind);
      if (!Kind) {
        err = createErrorRequestInvalid("missing 'key.kind'");
        return true;
      }
      CCInfo.Kind = Kind;

      SmallVector<sourcekitd_uid_t, 3> contexts;
      if (dict.getUIDArray(KeyContext, contexts, false)) {
        err = createErrorRequestInvalid("missing 'key.context'");
        return true;
      }

      for (auto context : contexts) {
        if (context == KindExpr) {
          CCInfo.Contexts |= CustomCompletionInfo::Expr;
        } else if (context == KindStmt) {
          CCInfo.Contexts |= CustomCompletionInfo::Stmt;
        } else if (context == KindType) {
          CCInfo.Contexts |= CustomCompletionInfo::Type;
        } else {
          err = createErrorRequestInvalid("invalid value for 'key.context'");
          return true;
        }
      }

      customCompletions.push_back(std::move(CCInfo));
      return false;
    });

    if (failed)
      return Rec(err);

    LangSupport &Lang = getGlobalContext().getSwiftLangSupport();
    Lang.codeCompleteSetCustom(customCompletions);
    return Rec(ResponseBuilder().createResponse());
  }

  if (!SourceFile.hasValue() && !SourceText.hasValue() &&
      ReqUID != RequestCodeCompleteUpdate)
    return Rec(createErrorRequestInvalid(
        "missing 'key.sourcefile' or 'key.sourcetext'"));

  // Requests that need semantic typechecking.

  // Typechecking arrays can blow up the stack currently.
  // Run them under a malloc'ed stack.

  static WorkQueue SemaQueue{ WorkQueue::Dequeuing::Concurrent,
                              "sourcekit.request.semantic" };
  sourcekitd_request_retain(ReqObj);
  SemaQueue.dispatch(
    [ReqObj, Rec, ReqUID, SourceFile, SourceText, Args] {
      RequestDict Req(ReqObj);
      handleSemanticRequest(Req, Rec, ReqUID, SourceFile, SourceText, Args);
      sourcekitd_request_release(ReqObj);
    },
    /*isStackDeep=*/true);
}

static void
handleSemanticRequest(RequestDict Req,
                      ResponseReceiver Rec,
                      sourcekitd_uid_t ReqUID,
                      Optional<StringRef> SourceFile,
                      Optional<StringRef> SourceText,
                      ArrayRef<const char *> Args) {

  llvm::SmallString<64> ErrBuf;

  if (ReqUID == RequestCodeComplete) {
    std::unique_ptr<llvm::MemoryBuffer>
    InputBuf = getInputBufForRequest(SourceFile, SourceText, ErrBuf);
    if (!InputBuf)
      return Rec(createErrorRequestFailed(ErrBuf.c_str()));
    int64_t Offset;
    if (Req.getInt64(KeyOffset, Offset, /*isOptional=*/false))
      return Rec(createErrorRequestInvalid("missing 'key.offset'"));
    return Rec(codeComplete(InputBuf.get(), Offset, Args));
  }

  if (ReqUID == RequestCodeCompleteOpen) {
    std::unique_ptr<llvm::MemoryBuffer> InputBuf =
        getInputBufForRequest(SourceFile, SourceText, ErrBuf);
    if (!InputBuf)
      return Rec(createErrorRequestFailed(ErrBuf.c_str()));
    Optional<StringRef> Name = Req.getString(KeyName);
    if (!Name.hasValue())
      return Rec(createErrorRequestInvalid("missing 'key.name'"));
    int64_t Offset;
    if (Req.getInt64(KeyOffset, Offset, /*isOptional=*/false))
      return Rec(createErrorRequestInvalid("missing 'key.offset'"));
    Optional<RequestDict> options = Req.getDictionary(KeyCodeCompleteOptions);
    return Rec(codeCompleteOpen(*Name, InputBuf.get(), Offset, options, Args));
  }

  if (ReqUID == RequestCodeCompleteUpdate) {
    Optional<StringRef> Name = Req.getString(KeyName);
    if (!Name.hasValue())
      return Rec(createErrorRequestInvalid("missing 'key.name'"));
    int64_t Offset;
    if (Req.getInt64(KeyOffset, Offset, /*isOptional=*/false))
      return Rec(createErrorRequestInvalid("missing 'key.offset'"));
    Optional<RequestDict> options = Req.getDictionary(KeyCodeCompleteOptions);
    return Rec(codeCompleteUpdate(*Name, Offset, options));
  }

  if (!SourceFile.hasValue())
    return Rec(createErrorRequestInvalid("missing 'key.sourcefile'"));

  if (ReqUID == RequestIndex) {
    StringRef Hash;
    Optional<StringRef> HashOpt = Req.getString(KeyHash);
    if (HashOpt.hasValue()) Hash = *HashOpt;
    return Rec(indexSource(*SourceFile, Args, Hash));
  }

  if (isSemanticEditorDisabled())
      return Rec(createErrorRequestFailed("semantic editor is disabled"));

  if (ReqUID == RequestCursorInfo) {
    int64_t Offset;
    if (Req.getInt64(KeyOffset, Offset, /*isOptional=*/false))
      return Rec(createErrorRequestInvalid("missing 'key.offset'"));
    return reportCursorInfo(*SourceFile, Offset, Args, Rec);
  }

  if (ReqUID == RequestRelatedIdents) {
    int64_t Offset;
    if (Req.getInt64(KeyOffset, Offset, /*isOptional=*/false))
      return Rec(createErrorRequestInvalid("missing 'key.offset'"));
    return findRelatedIdents(*SourceFile, Offset, Args, Rec);
  }

  {
    llvm::raw_svector_ostream OSErr(ErrBuf);
    OSErr << "unknown request: " << UIdentFromSKDUID(ReqUID).getName();
  }
  return Rec(createErrorRequestInvalid(ErrBuf.c_str()));
}

//============================================================================//
// Index
//============================================================================//

namespace {
class SKIndexingConsumer : public IndexingConsumer {
  struct Entity {
    UIdent Kind;
    ResponseBuilder::Dictionary Data;
    ResponseBuilder::Array Entities;
    ResponseBuilder::Array Related;
  };
  SmallVector<Entity, 6> EntitiesStack;

  struct Dependency {
    UIdent Kind;
    ResponseBuilder::Dictionary Data;
    ResponseBuilder::Array Dependencies;
  };
  SmallVector<Dependency, 6> DependenciesStack;

  ResponseBuilder::Dictionary TopDict;
  bool Cancelled = false;

public:
  std::string ErrorDescription;

  explicit SKIndexingConsumer(ResponseBuilder &RespBuilder) {
    TopDict = RespBuilder.getDictionary();

    // First in stack is the top-level "key.entities" container.
    EntitiesStack.push_back(
        { UIdent(),
          TopDict,
          ResponseBuilder::Array(),
          ResponseBuilder::Array() });

    DependenciesStack.push_back({UIdent(), TopDict, ResponseBuilder::Array() });
  }
  ~SKIndexingConsumer() {
    assert(Cancelled ||
           (EntitiesStack.size() == 1 && DependenciesStack.size() == 1));
  }

  void failed(StringRef ErrDescription) override;

  bool recordHash(StringRef Hash, bool isKnown) override;

  bool startDependency(UIdent Kind,
                       StringRef Name,
                       StringRef Path,
                       bool IsSystem,
                       StringRef Hash) override;

  bool finishDependency(UIdent Kind) override;

  bool startSourceEntity(const EntityInfo &Info) override;

  bool recordRelatedEntity(const EntityInfo &Info) override;

  bool finishSourceEntity(UIdent Kind) override;
};
}

static sourcekitd_response_t indexSource(StringRef Filename,
                                         ArrayRef<const char *> Args,
                                         StringRef KnownHash) {
  ResponseBuilder RespBuilder;
  SKIndexingConsumer IdxConsumer(RespBuilder);
  LangSupport &Lang = getGlobalContext().getSwiftLangSupport();
  Lang.indexSource(Filename, IdxConsumer, Args, KnownHash);

  if (!IdxConsumer.ErrorDescription.empty())
    return createErrorRequestFailed(IdxConsumer.ErrorDescription.c_str());

  return RespBuilder.createResponse();
}

void SKIndexingConsumer::failed(StringRef ErrDescription) {
  ErrorDescription = ErrDescription;
}

bool SKIndexingConsumer::recordHash(StringRef Hash, bool isKnown) {
  assert(!Hash.empty());
  TopDict.set(KeyHash, Hash);
  if (!isKnown) {
    // If the hash is known key.entities should be missing otherwise it should
    // exist, even as an empty array, so create it here.
    assert(EntitiesStack.size() == 1);
    Entity &Top = EntitiesStack.back();
    ResponseBuilder::Array &Arr = Top.Entities;
    assert(Arr.isNull());
    Arr = Top.Data.setArray(KeyEntities);
  }
  return true;
}

bool SKIndexingConsumer::startDependency(UIdent Kind,
                                         StringRef Name,
                                         StringRef Path,
                                         bool IsSystem,
                                         StringRef Hash) {
  Dependency &Parent = DependenciesStack.back();
  ResponseBuilder::Array &Arr = Parent.Dependencies;
  if (Arr.isNull())
    Arr = Parent.Data.setArray(KeyDependencies);

  auto Elem = Arr.appendDictionary();
  Elem.set(KeyKind, Kind);
  Elem.set(KeyName, Name);
  Elem.set(KeyFilePath, Path);
  if (IsSystem)
    Elem.setBool(KeyIsSystem, IsSystem);
  if (!Hash.empty())
    Elem.set(KeyHash, Hash);

  DependenciesStack.push_back({ Kind, Elem, ResponseBuilder::Array() });
  return true;
}

bool SKIndexingConsumer::finishDependency(UIdent Kind) {
  assert(DependenciesStack.back().Kind == Kind);
  DependenciesStack.pop_back();
  return true;
}

bool SKIndexingConsumer::startSourceEntity(const EntityInfo &Info) {
  Entity &Parent = EntitiesStack.back();
  ResponseBuilder::Array &Arr = Parent.Entities;
  if (Arr.isNull())
    Arr = Parent.Data.setArray(KeyEntities);

  auto Elem = Arr.appendDictionary();
  Elem.set(KeyKind, Info.Kind);
  if (!Info.Name.empty())
    Elem.set(KeyName, Info.Name);
  if (!Info.USR.empty())
    Elem.set(KeyUSR, Info.USR);
  if (Info.Line != 0) {
    assert(Info.Column != 0);
    Elem.set(KeyLine, Info.Line);
    Elem.set(KeyColumn, Info.Column);
  }

  if (Info.EntityType == EntityInfo::FuncDecl) {
    const FuncDeclEntityInfo &FDInfo =
        static_cast<const FuncDeclEntityInfo &>(Info);
    if (FDInfo.IsTestCandidate)
      Elem.setBool(KeyIsTestCandidate, true);
  }

  if (Info.EntityType == EntityInfo::CallReference) {
    const CallRefEntityInfo &CRInfo =
        static_cast<const CallRefEntityInfo &>(Info);
    if (!CRInfo.ReceiverUSR.empty())
      Elem.set(KeyReceiverUSR, CRInfo.ReceiverUSR);
    if (CRInfo.IsDynamic)
      Elem.setBool(KeyIsDynamic, true);
  }

  EntitiesStack.push_back({ Info.Kind, Elem, ResponseBuilder::Array(),
                            ResponseBuilder::Array()});
  return true;
}

bool SKIndexingConsumer::recordRelatedEntity(const EntityInfo &Info) {
  assert(EntitiesStack.size() > 1 && "Related entity at top-level ?");
  Entity &Parent = EntitiesStack.back();
  ResponseBuilder::Array &Arr = Parent.Related;
  if (Arr.isNull())
    Arr = Parent.Data.setArray(KeyRelated);

  auto Elem = Arr.appendDictionary();
  Elem.set(KeyKind, Info.Kind);
  if (!Info.Name.empty())
    Elem.set(KeyName, Info.Name);
  if (!Info.USR.empty())
    Elem.set(KeyUSR, Info.USR);
  if (Info.Line != 0) {
    assert(Info.Column != 0);
    Elem.set(KeyLine, Info.Line);
    Elem.set(KeyColumn, Info.Column);
  }

  return true;
}

bool SKIndexingConsumer::finishSourceEntity(UIdent Kind) {
  Entity &CurrEnt = EntitiesStack.back();
  assert(CurrEnt.Kind == Kind);
  EntitiesStack.pop_back();
  return true;
}

//============================================================================//
// ReportDocInfo
//============================================================================//

namespace {

class SKDocConsumer : public DocInfoConsumer {
  ResponseBuilder &RespBuilder;

  struct Entity {
    UIdent Kind;
    ResponseBuilder::Dictionary Data;
    ResponseBuilder::Array Entities;
    ResponseBuilder::Array Inherits;
    ResponseBuilder::Array Conforms;
    ResponseBuilder::Array Attrs;
  };
  SmallVector<Entity, 6> EntitiesStack;

  ResponseBuilder::Dictionary TopDict;
  ResponseBuilder::Array Diags;

  DocSupportAnnotationArrayBuilder AnnotationsBuilder;

  bool Cancelled = false;

  void addDocEntityInfoToDict(const DocEntityInfo &Info,
                              ResponseBuilder::Dictionary Dict);
public:
  std::string ErrorDescription;

  explicit SKDocConsumer(ResponseBuilder &RespBuilder)
      : RespBuilder(RespBuilder) {
    TopDict = RespBuilder.getDictionary();

    // First in stack is the top-level "key.entities" container.
    EntitiesStack.push_back(
        { UIdent(),
          TopDict,
          ResponseBuilder::Array(),
          ResponseBuilder::Array(),
          ResponseBuilder::Array(),
          ResponseBuilder::Array() });
  }
  ~SKDocConsumer() {
    assert(Cancelled || EntitiesStack.size() == 1);
  }

  sourcekitd_response_t createResponse() {
    TopDict.setCustomBuffer(KeyAnnotations,
        CustomBufferKind::DocSupportAnnotationArray,
        AnnotationsBuilder.createBuffer());
    return RespBuilder.createResponse();
  }

  void failed(StringRef ErrDescription) override;

  bool handleSourceText(StringRef Text) override;

  bool handleAnnotation(const DocEntityInfo &Info) override;

  bool startSourceEntity(const DocEntityInfo &Info) override;

  bool handleInheritsEntity(const DocEntityInfo &Info) override;
  bool handleConformsToEntity(const DocEntityInfo &Info) override;
  bool handleExtendsEntity(const DocEntityInfo &Info) override;

  bool handleAvailableAttribute(const AvailableAttrInfo &Info) override;

  bool finishSourceEntity(UIdent Kind) override;

  bool handleDiagnostic(const DiagnosticEntryInfo &Info) override;
};
}

static sourcekitd_response_t reportDocInfo(llvm::MemoryBuffer *InputBuf,
                                           StringRef ModuleName,
                                           ArrayRef<const char *> Args) {
  ResponseBuilder RespBuilder;
  SKDocConsumer DocConsumer(RespBuilder);
  LangSupport &Lang = getGlobalContext().getSwiftLangSupport();
  Lang.getDocInfo(InputBuf, ModuleName, Args, DocConsumer);

  if (!DocConsumer.ErrorDescription.empty())
    return createErrorRequestFailed(DocConsumer.ErrorDescription.c_str());

  return DocConsumer.createResponse();
}

void SKDocConsumer::addDocEntityInfoToDict(const DocEntityInfo &Info,
                                           ResponseBuilder::Dictionary Elem) {
  Elem.set(KeyKind, Info.Kind);
  if (!Info.Name.empty())
    Elem.set(KeyName, Info.Name);
  if (!Info.Argument.empty())
    Elem.set(KeyKeyword, Info.Argument);
  if (!Info.USR.empty())
    Elem.set(KeyUSR, Info.USR);
  if (Info.Length > 0) {
    Elem.set(KeyOffset, Info.Offset);
    Elem.set(KeyLength, Info.Length);
  }
  if (Info.IsUnavailable)
    Elem.set(KeyIsUnavailable, Info.IsUnavailable);
  if (Info.IsDeprecated)
    Elem.set(KeyIsDeprecated, Info.IsDeprecated);
  if (!Info.DocComment.empty())
    Elem.set(KeyDocFullAsXML, Info.DocComment);

  if (!Info.GenericParams.empty()) {
    auto GPArray = Elem.setArray(KeyGenericParams);
    for (auto &GP : Info.GenericParams) {
      auto GPElem = GPArray.appendDictionary();
      GPElem.set(KeyName, GP.Name);
      if (!GP.Inherits.empty())
        GPElem.set(KeyInherits, GP.Inherits);
    }
  }
  // Note that due to protocol extensions, GenericRequirements may be non-empty
  // while GenericParams is empty.
  if (!Info.GenericRequirements.empty()) {
    auto ReqArray = Elem.setArray(KeyGenericRequirements);
    for (auto &Req : Info.GenericRequirements) {
      auto ReqElem = ReqArray.appendDictionary();
      ReqElem.set(KeyDescription, Req);
    }
  }
}

void SKDocConsumer::failed(StringRef ErrDescription) {
  ErrorDescription = ErrDescription;
}

bool SKDocConsumer::handleSourceText(StringRef Text) {
  TopDict.set(KeySourceText, Text);
  return true;
}

bool SKDocConsumer::handleAnnotation(const DocEntityInfo &Info) {
  AnnotationsBuilder.add(Info);
  return true;
}

bool SKDocConsumer::startSourceEntity(const DocEntityInfo &Info) {
  Entity &Parent = EntitiesStack.back();
  ResponseBuilder::Array &Arr = Parent.Entities;
  if (Arr.isNull())
    Arr = Parent.Data.setArray(KeyEntities);

  auto Elem = Arr.appendDictionary();
  addDocEntityInfoToDict(Info, Elem);

  EntitiesStack.push_back({ Info.Kind, Elem, ResponseBuilder::Array(),
                            ResponseBuilder::Array(),
                            ResponseBuilder::Array(),
                            ResponseBuilder::Array()});
  return true;
}

bool SKDocConsumer::handleInheritsEntity(const DocEntityInfo &Info) {
  assert(EntitiesStack.size() > 1 && "Related entity at top-level ?");
  Entity &Parent = EntitiesStack.back();
  ResponseBuilder::Array &Arr = Parent.Inherits;
  if (Arr.isNull())
    Arr = Parent.Data.setArray(KeyInherits);

  addDocEntityInfoToDict(Info, Arr.appendDictionary());
  return true;
}

bool SKDocConsumer::handleConformsToEntity(const DocEntityInfo &Info) {
  assert(EntitiesStack.size() > 1 && "Related entity at top-level ?");
  Entity &Parent = EntitiesStack.back();
  ResponseBuilder::Array &Arr = Parent.Conforms;
  if (Arr.isNull())
    Arr = Parent.Data.setArray(KeyConforms);

  addDocEntityInfoToDict(Info, Arr.appendDictionary());
  return true;
}

bool SKDocConsumer::handleExtendsEntity(const DocEntityInfo &Info) {
  assert(EntitiesStack.size() > 1 && "Related entity at top-level ?");
  Entity &Parent = EntitiesStack.back();
  addDocEntityInfoToDict(Info, Parent.Data.setDictionary(KeyExtends));
  return true;
}

bool SKDocConsumer::handleAvailableAttribute(const AvailableAttrInfo &Info) {
  Entity &Parent = EntitiesStack.back();
  ResponseBuilder::Array &Arr = Parent.Attrs;
  if (Arr.isNull())
    Arr = Parent.Data.setArray(KeyAttributes);

  auto Elem = Arr.appendDictionary();
  Elem.set(KeyKind, Info.AttrKind);
  if (Info.IsUnavailable)
    Elem.set(KeyIsUnavailable, Info.IsUnavailable);
  if (Info.IsDeprecated)
    Elem.set(KeyIsDeprecated, Info.IsDeprecated);
  if (Info.Platform.isValid())
    Elem.set(KeyPlatform, Info.Platform);
  if (!Info.Message.empty())
    Elem.set(KeyMessage, Info.Message);
  if (Info.Introduced.hasValue())
    Elem.set(KeyIntroduced, Info.Introduced.getValue().getAsString());
  if (Info.Deprecated.hasValue())
    Elem.set(KeyDeprecated, Info.Deprecated.getValue().getAsString());
  if (Info.Obsoleted.hasValue())
    Elem.set(KeyObsoleted, Info.Obsoleted.getValue().getAsString());
  
  return true;
}

bool SKDocConsumer::finishSourceEntity(UIdent Kind) {
  Entity &CurrEnt = EntitiesStack.back();
  assert(CurrEnt.Kind == Kind);
  EntitiesStack.pop_back();
  return true;
}

bool SKDocConsumer::handleDiagnostic(const DiagnosticEntryInfo &Info) {
  ResponseBuilder::Array &Arr = Diags;
  if (Arr.isNull())
    Arr = TopDict.setArray(KeyDiagnostics);

  auto Elem = Arr.appendDictionary();
  UIdent SeverityUID;
  switch (Info.Severity) {
  case DiagnosticSeverityKind::Warning:
    SeverityUID = DiagKindWarning;
    break;
  case DiagnosticSeverityKind::Error:
    SeverityUID = DiagKindError;
    break;
  }

  Elem.set(KeySeverity, SeverityUID);
  fillDictionaryForDiagnosticInfo(Elem, Info);

  if (!Info.Notes.empty()) {
    auto NotesArr = Elem.setArray(KeyDiagnostics);
    for (auto &NoteDiag : Info.Notes) {
      auto NoteElem = NotesArr.appendDictionary();
      NoteElem.set(KeySeverity, DiagKindNote);
      fillDictionaryForDiagnosticInfo(NoteElem, NoteDiag);
    }
  }

  return true;
}

//============================================================================//
// ReportCursorInfo
//============================================================================//

static void reportCursorInfo(StringRef Filename,
                             int64_t Offset,
                             ArrayRef<const char *> Args,
                             ResponseReceiver Rec) {
  LangSupport &Lang = getGlobalContext().getSwiftLangSupport();
  Lang.getCursorInfo(Filename, Offset, Args, [Rec](const CursorInfo &Info) {
    if (Info.IsCancelled)
      return Rec(createErrorRequestCancelled());

    ResponseBuilder RespBuilder;
    if (Info.Kind.isInvalid())
      return Rec(RespBuilder.createResponse());

    auto Elem = RespBuilder.getDictionary();
    Elem.set(KeyKind, Info.Kind);
    Elem.set(KeyName, Info.Name);
    if (!Info.USR.empty())
      Elem.set(KeyUSR, Info.USR);
    if (!Info.TypeName.empty())
      Elem.set(KeyTypeName, Info.TypeName);
    if (!Info.DocComment.empty())
      Elem.set(KeyDocFullAsXML, Info.DocComment);
    if (!Info.AnnotatedDeclaration.empty())
      Elem.set(KeyAnnotatedDecl, Info.AnnotatedDeclaration);
    if (!Info.ModuleName.empty())
      Elem.set(KeyModuleName, Info.ModuleName);
    if (!Info.ModuleInterfaceName.empty())
      Elem.set(KeyModuleInterfaceName, Info.ModuleInterfaceName);
    if (Info.DeclarationLoc.hasValue()) {
      Elem.set(KeyOffset, Info.DeclarationLoc.getValue().first);
      Elem.set(KeyLength, Info.DeclarationLoc.getValue().second);
      if (!Info.Filename.empty())
        Elem.set(KeyFilePath, Info.Filename);
    }
    if (!Info.OverrideUSRs.empty()) {
      auto Overrides = Elem.setArray(KeyOverrides);
      for (auto USR : Info.OverrideUSRs) {
        auto Override = Overrides.appendDictionary();
        Override.set(KeyUSR, USR);
      }
    }
    if (!Info.AnnotatedRelatedDeclarations.empty()) {
      auto RelDecls = Elem.setArray(KeyRelatedDecls);
      for (auto AnnotDecl : Info.AnnotatedRelatedDeclarations) {
        auto RelDecl = RelDecls.appendDictionary();
        RelDecl.set(KeyAnnotatedDecl, AnnotDecl);
      }
    }
    if (Info.IsSystem)
      Elem.setBool(KeyIsSystem, true);
    if (!Info.TypeInteface.empty())
      Elem.set(KeyTypeInterface, Info.TypeInteface);

    return Rec(RespBuilder.createResponse());
  });
}

//============================================================================//
// FindRelatedIdents
//============================================================================//

static void findRelatedIdents(StringRef Filename,
                              int64_t Offset,
                              ArrayRef<const char *> Args,
                              ResponseReceiver Rec) {
  LangSupport &Lang = getGlobalContext().getSwiftLangSupport();
  Lang.findRelatedIdentifiersInFile(Filename, Offset, Args,
                                    [Rec](const RelatedIdentsInfo &Info) {
    if (Info.IsCancelled)
      return Rec(createErrorRequestCancelled());

    ResponseBuilder RespBuilder;
    auto Arr = RespBuilder.getDictionary().setArray(KeyResults);
    for (auto R : Info.Ranges) {
      auto Elem = Arr.appendDictionary();
      Elem.set(KeyOffset, R.first);
      Elem.set(KeyLength, R.second);
    }

    Rec(RespBuilder.createResponse());
  });
}

//============================================================================//
// CodeComplete
//============================================================================//

namespace {
class SKCodeCompletionConsumer : public CodeCompletionConsumer {
  ResponseBuilder &RespBuilder;
  CodeCompletionResultsArrayBuilder ResultsBuilder;

  std::string ErrorDescription;

public:
  explicit SKCodeCompletionConsumer(ResponseBuilder &RespBuilder)
    : RespBuilder(RespBuilder) {
  }

  sourcekitd_response_t createResponse() {
    if (!ErrorDescription.empty())
      return createErrorRequestFailed(ErrorDescription.c_str());

    RespBuilder.getDictionary().setCustomBuffer(KeyResults,
        CustomBufferKind::CodeCompletionResultsArray,
        ResultsBuilder.createBuffer());
    return RespBuilder.createResponse();
  }


  void failed(StringRef ErrDescription) override;

  bool handleResult(const CodeCompletionInfo &Info) override;
};
}

static sourcekitd_response_t codeComplete(llvm::MemoryBuffer *InputBuf,
                                          int64_t Offset,
                                          ArrayRef<const char *> Args) {
  ResponseBuilder RespBuilder;
  SKCodeCompletionConsumer CCC(RespBuilder);
  LangSupport &Lang = getGlobalContext().getSwiftLangSupport();
  Lang.codeComplete(InputBuf, Offset, CCC, Args);
  return CCC.createResponse();
}

void SKCodeCompletionConsumer::failed(StringRef ErrDescription) {
  ErrorDescription = ErrDescription;
}

bool SKCodeCompletionConsumer::handleResult(const CodeCompletionInfo &R) {
  Optional<StringRef> ModuleNameOpt;
  if (!R.ModuleName.empty())
    ModuleNameOpt = R.ModuleName;
  Optional<StringRef> DocBriefOpt;
  if (!R.DocBrief.empty())
    DocBriefOpt = R.DocBrief;
  Optional<StringRef> AssocUSRsOpt;
  if (!R.AssocUSRs.empty())
    AssocUSRsOpt = R.AssocUSRs;

  assert(!R.ModuleImportDepth && "not implemented on CompactArray path");

  ResultsBuilder.add(R.Kind,
                     R.Name,
                     R.Description,
                     R.SourceText,
                     R.TypeName,
                     ModuleNameOpt,
                     DocBriefOpt,
                     AssocUSRsOpt,
                     R.SemanticContext,
                     R.NotRecommended,
                     R.NumBytesToErase);
  return true;
}


//============================================================================//
// (New) CodeComplete
//============================================================================//

namespace {
class SKGroupedCodeCompletionConsumer : public GroupedCodeCompletionConsumer {
  ResponseBuilder &RespBuilder;
  ResponseBuilder::Dictionary Response;
  SmallVector<ResponseBuilder::Array, 3> GroupContentsStack;
  std::string ErrorDescription;

public:
  explicit SKGroupedCodeCompletionConsumer(ResponseBuilder &RespBuilder)
      : RespBuilder(RespBuilder) {}

  sourcekitd_response_t createResponse() {
    if (!ErrorDescription.empty())
      return createErrorRequestFailed(ErrorDescription.c_str());
    assert(GroupContentsStack.empty() && "mismatched start/endGroup");
    return RespBuilder.createResponse();
  }

  void failed(StringRef ErrDescription) override;
  bool handleResult(const CodeCompletionInfo &Info) override;
  void startGroup(UIdent kind, StringRef name) override;
  void endGroup() override;
  void setNextRequestStart(unsigned offset) override;
};

class SKOptionsDictionary : public OptionsDictionary {
  RequestDict &Options;

public:
  explicit SKOptionsDictionary(RequestDict &Options) : Options(Options) {}

  bool valueForOption(UIdent Key, unsigned &Val) override {
    int64_t result;
    if (Options.getInt64(Key, result, false))
      return false;
    Val = static_cast<unsigned>(result);
    return true;
  }

  bool valueForOption(UIdent Key, bool &Val) override {
    int64_t result;
    if (Options.getInt64(Key, result, false))
      return false;
    Val = result ? true : false;
    return true;
  }

  bool valueForOption(UIdent Key, StringRef &Val) override {
    Optional<StringRef> value = Options.getString(Key);
    if (!value)
      return false;
    Val = *value;
    return true;
  }
};
} // end anonymous namespace

static sourcekitd_response_t codeCompleteOpen(StringRef Name,
                                              llvm::MemoryBuffer *InputBuf,
                                              int64_t Offset,
                                              Optional<RequestDict> optionsDict,
                                              ArrayRef<const char *> Args) {
  ResponseBuilder RespBuilder;
  SKGroupedCodeCompletionConsumer CCC(RespBuilder);
  std::unique_ptr<SKOptionsDictionary> options;
  if (optionsDict)
    options = std::move(llvm::make_unique<SKOptionsDictionary>(*optionsDict));
  LangSupport &Lang = getGlobalContext().getSwiftLangSupport();
  Lang.codeCompleteOpen(Name, InputBuf, Offset, options.get(), CCC, Args);
  return CCC.createResponse();
}

static sourcekitd_response_t codeCompleteClose(StringRef Name, int64_t Offset) {
  ResponseBuilder RespBuilder;
  SKGroupedCodeCompletionConsumer CCC(RespBuilder);
  LangSupport &Lang = getGlobalContext().getSwiftLangSupport();
  Lang.codeCompleteClose(Name, Offset, CCC);
  return CCC.createResponse();
}

static sourcekitd_response_t
codeCompleteUpdate(StringRef name, int64_t offset,
                   Optional<RequestDict> optionsDict) {
  ResponseBuilder RespBuilder;
  SKGroupedCodeCompletionConsumer CCC(RespBuilder);
  std::unique_ptr<SKOptionsDictionary> options;
  if (optionsDict)
    options = std::move(llvm::make_unique<SKOptionsDictionary>(*optionsDict));
  LangSupport &Lang = getGlobalContext().getSwiftLangSupport();
  Lang.codeCompleteUpdate(name, offset, options.get(), CCC);
  return CCC.createResponse();
}

void SKGroupedCodeCompletionConsumer::failed(StringRef ErrDescription) {
  ErrorDescription = ErrDescription;
}

bool SKGroupedCodeCompletionConsumer::handleResult(const CodeCompletionInfo &R) {
  assert(!GroupContentsStack.empty() && "missing root group");

  auto result = GroupContentsStack.back().appendDictionary();
  if (R.CustomKind)
    result.set(KeyKind, sourcekitd_uid_t(R.CustomKind));
  else
    result.set(KeyKind, R.Kind);
  result.set(KeyName, R.Name);
  result.set(KeyDescription, R.Description);
  result.set(KeySourceText, R.SourceText);
  result.set(KeyTypeName, R.TypeName);
  result.set(KeyContext, R.SemanticContext);
  if (!R.ModuleName.empty())
    result.set(KeyModuleName, R.ModuleName);
  if (!R.DocBrief.empty())
    result.set(KeyDocBrief, R.DocBrief);
  if (!R.AssocUSRs.empty())
    result.set(KeyAssociatedUSRs, R.AssocUSRs);
  if (R.ModuleImportDepth)
    result.set(KeyModuleImportDepth, *R.ModuleImportDepth);
  if (R.NotRecommended)
    result.set(KeyNotRecommended, R.NotRecommended);
  result.set(KeyNumBytesToErase, R.NumBytesToErase);

  if (R.descriptionStructure) {
    auto addRange = [](ResponseBuilder::Dictionary dict, UIdent offset,
                       UIdent length, CodeCompletionInfo::IndexRange range) {
      if (!range.empty()) {
        dict.set(offset, range.begin);
        dict.set(length, range.length());
      }
    };

    auto structure = result.setDictionary(KeySubStructure);
    addRange(structure, KeyNameOffset, KeyNameLength,
             R.descriptionStructure->baseName);
    addRange(structure, KeyBodyOffset, KeyBodyLength,
             R.descriptionStructure->parameterRange);
    addRange(structure, KeyThrowOffset, KeyThrowLength,
             R.descriptionStructure->throwsRange);

    if (R.parametersStructure) {
      auto params = structure.setArray(KeySubStructure);
      for (auto &P : *R.parametersStructure) {
        auto param = params.appendDictionary();
        addRange(param, KeyNameOffset, KeyNameLength, P.name);
        addRange(param, KeyBodyOffset, KeyBodyLength, P.afterColon);
        if (P.isLocalName)
          param.set(KeyIsLocal, true);
      }
    }
  }

  return true;
}

void SKGroupedCodeCompletionConsumer::startGroup(UIdent kind, StringRef name) {
  ResponseBuilder::Dictionary group;
  if (GroupContentsStack.empty()) {
    group = RespBuilder.getDictionary();
    Response = group;
  } else {
    group = GroupContentsStack.back().appendDictionary();
  }
  group.set(KeyKind, kind);
  group.set(KeyName, name);
  auto contents = group.setArray(KeyResults);
  GroupContentsStack.push_back(contents);
}
void SKGroupedCodeCompletionConsumer::endGroup() {
  assert(!GroupContentsStack.empty());
  GroupContentsStack.pop_back();
}
void SKGroupedCodeCompletionConsumer::setNextRequestStart(unsigned offset) {
  assert(!Response.isNull());
  Response.set(KeyNextRequestStart, offset);
}

//============================================================================//
// Editor
//============================================================================//

namespace {
class SKEditorConsumer : public EditorConsumer {
  ResponseReceiver RespReceiver;
  ResponseBuilder RespBuilder;

  ResponseBuilder::Dictionary Dict;
  TokenAnnotationsArrayBuilder SyntaxMap;
  TokenAnnotationsArrayBuilder SemanticAnnotations;

  struct StructureNode {
    ResponseBuilder::Dictionary Dict;
    ResponseBuilder::Array SubStructures;
    ResponseBuilder::Array Elements;

    explicit StructureNode(ResponseBuilder::Dictionary Dict) : Dict(Dict) {}
  };
  std::vector<StructureNode> Structures;
  ResponseBuilder::Array Diags;
  sourcekitd_response_t Error = nullptr;

  bool EnableSyntaxMap;
  bool EnableDiagnostics;
  bool SyntacticOnly;

public:
  SKEditorConsumer(bool EnableSyntaxMap,
                   bool EnableStructure, bool EnableDiagnostics,
                   bool SyntacticOnly)
  : EnableSyntaxMap(EnableSyntaxMap),
    EnableDiagnostics(EnableDiagnostics),
    SyntacticOnly(SyntacticOnly) {

    Dict = RespBuilder.getDictionary();
    if (EnableStructure)
      Structures.push_back(StructureNode{Dict});
  }

  SKEditorConsumer(ResponseReceiver RespReceiver, bool EnableSyntaxMap,
                   bool EnableStructure, bool EnableDiagnostics,
                   bool SyntacticOnly)
  : SKEditorConsumer(EnableSyntaxMap, EnableStructure,
                     EnableDiagnostics, SyntacticOnly) {
    this->RespReceiver = RespReceiver;
  }

  sourcekitd_response_t createResponse();

  bool needsSemanticInfo() override {
    return !SyntacticOnly && !isSemanticEditorDisabled();
  }

  void handleRequestError(const char *Description) override;

  bool handleSyntaxMap(unsigned Offset, unsigned Length, UIdent Kind) override;

  bool handleSemanticAnnotation(unsigned Offset, unsigned Length, UIdent Kind,
                                bool isSystem) override;

  bool beginDocumentSubStructure(unsigned Offset, unsigned Length, UIdent Kind,
                                 UIdent AccessLevel,
                                 UIdent SetterAccessLevel,
                                 unsigned NameOffset,
                                 unsigned NameLength,
                                 unsigned BodyOffset,
                                 unsigned BodyLength,
                                 StringRef DisplayName,
                                 StringRef TypeName,
                                 StringRef RuntimeName,
                                 StringRef SelectorName,
                                 ArrayRef<StringRef> InheritedTypes,
                                 ArrayRef<UIdent> Attrs) override;

  bool endDocumentSubStructure() override;

  bool handleDocumentSubStructureElement(UIdent Kind,
                                         unsigned Offset,
                                         unsigned Length) override;

  bool recordAffectedRange(unsigned Offset, unsigned Length) override;
  
  bool recordAffectedLineRange(unsigned Line, unsigned Length) override;

  bool recordFormattedText(StringRef Text) override;

  bool setDiagnosticStage(UIdent DiagStage) override;
  bool handleDiagnostic(const DiagnosticEntryInfo &Info,
                        UIdent DiagStage) override;

  bool handleSourceText(StringRef Text) override;

  virtual void finished() override {
    if (RespReceiver)
      RespReceiver(createResponse());
  }
};

}

static sourcekitd_response_t
editorOpen(StringRef Name, llvm::MemoryBuffer *Buf, bool EnableSyntaxMap,
           bool EnableStructure, bool EnableDiagnostics, bool SyntacticOnly,
           ArrayRef<const char *> Args) {
  SKEditorConsumer EditC(EnableSyntaxMap, EnableStructure,
                         EnableDiagnostics, SyntacticOnly);
  LangSupport &Lang = getGlobalContext().getSwiftLangSupport();
  Lang.editorOpen(Name, Buf, EnableSyntaxMap, EditC, Args);
  return EditC.createResponse();
}

static sourcekitd_response_t
editorOpenInterface(StringRef Name, StringRef ModuleName,
                    ArrayRef<const char *> Args) {
  SKEditorConsumer EditC(/*EnableSyntaxMap=*/true,
                         /*EnableStructure=*/true,
                         /*EnableDiagnostics=*/false,
                         /*SyntacticOnly=*/false);
  LangSupport &Lang = getGlobalContext().getSwiftLangSupport();
  Lang.editorOpenInterface(EditC, Name, ModuleName, Args);
  return EditC.createResponse();
}


/// Getting the interface from a swift source file differs from getting interfaces
/// from headers or modules for its performing asynchronously.
static void
editorOpenSwiftSourceInterface(StringRef Name, StringRef HeaderName,
                               ArrayRef<const char *> Args,
                               ResponseReceiver Rec) {
  auto EditC = std::make_shared<SKEditorConsumer>(Rec,
                                                  /*EnableSyntaxMap=*/true,
                                                  /*EnableStructure=*/true,
                                                  /*EnableDiagnostics=*/false,
                                                  /*SyntacticOnly=*/false);
  LangSupport &Lang = getGlobalContext().getSwiftLangSupport();
  Lang.editorOpenSwiftSourceInterface(Name, HeaderName, Args, EditC);
}

static sourcekitd_response_t editorExtractTextFromComment(StringRef Source) {
  SKEditorConsumer EditC(/*EnableSyntaxMap=*/false,
                         /*EnableStructure=*/false,
                         /*EnableDiagnostics=*/false,
                         /*SyntacticOnly=*/true);
  LangSupport &Lang = getGlobalContext().getSwiftLangSupport();
  Lang.editorExtractTextFromComment(Source, EditC);
  return EditC.createResponse();
}

static sourcekitd_response_t
editorOpenHeaderInterface(StringRef Name, StringRef HeaderName,
                          ArrayRef<const char *> Args) {
  SKEditorConsumer EditC(/*EnableSyntaxMap=*/true,
                         /*EnableStructure=*/true,
                         /*EnableDiagnostics=*/false,
                         /*SyntacticOnly=*/false);
  LangSupport &Lang = getGlobalContext().getSwiftLangSupport();
  Lang.editorOpenHeaderInterface(EditC, Name, HeaderName, Args);
  return EditC.createResponse();
}

static sourcekitd_response_t
editorClose(StringRef Name, bool RemoveCache) {
  ResponseBuilder RespBuilder;
  LangSupport &Lang = getGlobalContext().getSwiftLangSupport();
  Lang.editorClose(Name, RemoveCache);
  return RespBuilder.createResponse();
}

static sourcekitd_response_t
editorReplaceText(StringRef Name, llvm::MemoryBuffer *Buf, unsigned Offset,
                  unsigned Length, bool EnableSyntaxMap, bool EnableStructure,
                  bool EnableDiagnostics, bool SyntacticOnly) {
  SKEditorConsumer EditC(EnableSyntaxMap, EnableStructure,
                         EnableDiagnostics, SyntacticOnly);
  LangSupport &Lang = getGlobalContext().getSwiftLangSupport();
  Lang.editorReplaceText(Name, Buf, Offset, Length, EditC);
  return EditC.createResponse();
}

static void
editorApplyFormatOptions(StringRef Name, RequestDict &FmtOptions) {
  LangSupport &Lang = getGlobalContext().getSwiftLangSupport();
  SKOptionsDictionary SKFmtOptions(FmtOptions);
  Lang.editorApplyFormatOptions(Name, SKFmtOptions);
}

static sourcekitd_response_t
editorFormatText(StringRef Name, unsigned Line, unsigned Length) {
  SKEditorConsumer EditC(false, false, false,
                         /*SyntacticOnly=*/true);
  LangSupport &Lang = getGlobalContext().getSwiftLangSupport();
  Lang.editorFormatText(Name, Line, Length, EditC);
  return EditC.createResponse();
}

static sourcekitd_response_t
editorExpandPlaceholder(StringRef Name, unsigned Offset, unsigned Length) {
  SKEditorConsumer EditC(false, false, false,
                         /*SyntacticOnly=*/true);
  LangSupport &Lang = getGlobalContext().getSwiftLangSupport();
  Lang.editorExpandPlaceholder(Name, Offset, Length, EditC);
  return EditC.createResponse();
}

sourcekitd_response_t SKEditorConsumer::createResponse() {
  if (Error)
    return Error;

  if (EnableSyntaxMap) {
    Dict.setCustomBuffer(KeySyntaxMap,
        CustomBufferKind::TokenAnnotationsArray,
        SyntaxMap.createBuffer());
  }
  if (!SemanticAnnotations.empty()) {
    Dict.setCustomBuffer(KeyAnnotations,
        CustomBufferKind::TokenAnnotationsArray,
        SemanticAnnotations.createBuffer());
  }

  return RespBuilder.createResponse();
}

void SKEditorConsumer::handleRequestError(const char *Description) {
  if (!Error) {
    Error = createErrorRequestFailed(Description);
  }
  if (RespReceiver) {
    RespReceiver(Error);
    RespReceiver = ResponseReceiver();
  }
}

bool SKEditorConsumer::handleSyntaxMap(unsigned Offset, unsigned Length,
                                       UIdent Kind) {
  if (!EnableSyntaxMap)
    return true;

  SyntaxMap.add(Kind, Offset, Length, /*IsSystem=*/false);
  return true;
}

bool SKEditorConsumer::handleSemanticAnnotation(unsigned Offset,
                                                unsigned Length,
                                                UIdent Kind, bool isSystem) {
  assert(Kind.isValid());
  SemanticAnnotations.add(Kind, Offset, Length, isSystem);
  return true;
}

bool
SKEditorConsumer::beginDocumentSubStructure(unsigned Offset,
                                            unsigned Length, UIdent Kind,
                                            UIdent AccessLevel,
                                            UIdent SetterAccessLevel,
                                            unsigned NameOffset,
                                            unsigned NameLength,
                                            unsigned BodyOffset,
                                            unsigned BodyLength,
                                            StringRef DisplayName,
                                            StringRef TypeName,
                                            StringRef RuntimeName,
                                            StringRef SelectorName,
                                            ArrayRef<StringRef> InheritedTypes,
                                            ArrayRef<UIdent> Attrs) {
  if (Structures.empty())
    return true;

  auto &Parent = Structures.back();
  if (Parent.SubStructures.isNull())
    Parent.SubStructures = Parent.Dict.setArray(KeySubStructure);
  auto Node = Parent.SubStructures.appendDictionary();
  Node.set(KeyOffset, Offset);
  Node.set(KeyLength, Length);
  Node.set(KeyKind, Kind);
  if (AccessLevel.isValid())
    Node.set(KeyAccessibility, AccessLevel);
  if (SetterAccessLevel.isValid())
    Node.set(KeySetterAccessibility, SetterAccessLevel);
  Node.set(KeyNameOffset, NameOffset);
  Node.set(KeyNameLength, NameLength);
  if (BodyOffset != 0 || BodyLength !=0) {
    Node.set(KeyBodyOffset, BodyOffset);
    Node.set(KeyBodyLength, BodyLength);
  }

  if (!DisplayName.empty())
    Node.set(KeyName, DisplayName);

  if (!TypeName.empty())
    Node.set(KeyTypeName,TypeName);

  if (!RuntimeName.empty())
    Node.set(KeyRuntimeName, RuntimeName);

  if (!SelectorName.empty())
    Node.set(KeySelectorName, SelectorName);

  if (!InheritedTypes.empty()) {
    auto TypeArray = Node.setArray(KeyInheritedTypes);
    for (const StringRef &TypeName : InheritedTypes) {
      TypeArray.appendDictionary().set(KeyName, TypeName);
    }
  }
  if (!Attrs.empty()) {
    auto AttrArray = Node.setArray(KeyAttributes);
    for (auto Attr : Attrs) {
      auto AttrDict = AttrArray.appendDictionary();
      AttrDict.set(KeyAttribute, Attr);
    }
  }
  Structures.push_back(StructureNode{Node});
  return true;
}

bool SKEditorConsumer::endDocumentSubStructure() {
  if (!Structures.empty())
    Structures.pop_back();

  return true;
}

bool SKEditorConsumer::handleDocumentSubStructureElement(UIdent Kind,
                                                         unsigned Offset,
                                                         unsigned Length) {
  if (Structures.empty())
    return true;

  auto &Parent = Structures.back();
  if (Parent.Elements.isNull())
    Parent.Elements = Parent.Dict.setArray(KeyElements);
  auto Node = Parent.Elements.appendDictionary();
  Node.set(KeyKind, Kind);
  Node.set(KeyOffset, Offset);
  Node.set(KeyLength, Length);
  return  true;
}

bool SKEditorConsumer::recordAffectedRange(unsigned Offset, unsigned Length) {
  Dict.set(KeyOffset, Offset);
  Dict.set(KeyLength, Length);
  
  return true;
}

bool SKEditorConsumer::recordAffectedLineRange(unsigned Line, unsigned Length) {
  Dict.set(KeyLine, Line);
  Dict.set(KeyLength, Length);
  
  return true;
}

bool SKEditorConsumer::recordFormattedText(StringRef Text) {
  Dict.set(KeySourceText, Text);

  return true;
}

static void fillDictionaryForDiagnosticInfo(
    ResponseBuilder::Dictionary Elem, const DiagnosticEntryInfoBase &Info) {

  Elem.set(KeyDescription, Info.Description);
  if (Info.Line != 0) {
    Elem.set(KeyLine, Info.Line);
    Elem.set(KeyColumn, Info.Column);
  } else {
    Elem.set(KeyOffset, Info.Offset);
  }
  if (!Info.Filename.empty())
    Elem.set(KeyFilePath, Info.Filename);

  if (!Info.Ranges.empty()) {
    auto RangesArr = Elem.setArray(KeyRanges);
    for (auto R : Info.Ranges) {
      auto RangeElem = RangesArr.appendDictionary();
      RangeElem.set(KeyOffset, R.first);
      RangeElem.set(KeyLength, R.second);
    }
  }

  if (!Info.Fixits.empty()) {
    auto FixitsArr = Elem.setArray(KeyFixits);
    for (auto F : Info.Fixits) {
      auto FixitElem = FixitsArr.appendDictionary();
      FixitElem.set(KeyOffset, F.Offset);
      FixitElem.set(KeyLength, F.Length);
      FixitElem.set(KeySourceText, F.Text);
    }
  }
}

bool SKEditorConsumer::setDiagnosticStage(UIdent DiagStage) {
  Dict.set(KeyDiagnosticStage, DiagStage);
  return true;
}

bool SKEditorConsumer::handleDiagnostic(const DiagnosticEntryInfo &Info,
                                        UIdent DiagStage) {
  if (!EnableDiagnostics)
    return true;

  ResponseBuilder::Array &Arr = Diags;
  if (Arr.isNull())
    Arr = Dict.setArray(KeyDiagnostics);

  auto Elem = Arr.appendDictionary();
  UIdent SeverityUID;
  switch (Info.Severity) {
  case DiagnosticSeverityKind::Warning:
    SeverityUID = DiagKindWarning;
    break;
  case DiagnosticSeverityKind::Error:
    SeverityUID = DiagKindError;
    break;
  }

  Elem.set(KeySeverity, SeverityUID);
  Elem.set(KeyDiagnosticStage, DiagStage);
  fillDictionaryForDiagnosticInfo(Elem, Info);

  if (!Info.Notes.empty()) {
    auto NotesArr = Elem.setArray(KeyDiagnostics);
    for (auto &NoteDiag : Info.Notes) {
      auto NoteElem = NotesArr.appendDictionary();
      NoteElem.set(KeySeverity, DiagKindNote);
      fillDictionaryForDiagnosticInfo(NoteElem, NoteDiag);
    }
  }

  return true;
}

bool SKEditorConsumer::handleSourceText(StringRef Text) {
  Dict.set(KeySourceText, Text);
  return true;
}

static sourcekitd_response_t
editorFindUSR(StringRef DocumentName, StringRef USR) {
  ResponseBuilder RespBuilder;
  LangSupport &Lang = getGlobalContext().getSwiftLangSupport();
  llvm::Optional<std::pair<unsigned, unsigned>>
      Range = Lang.findUSRRange(DocumentName, USR);
  if (Range.hasValue()) {
    RespBuilder.getDictionary().set(KeyOffset, Range->first);
    RespBuilder.getDictionary().set(KeyLength, Range->second);
  }
  return RespBuilder.createResponse();
}

static sourcekitd_response_t
editorFindInterfaceDoc(StringRef ModuleName, ArrayRef<const char *> Args) {
  ResponseBuilder RespBuilder;
  sourcekitd_response_t Resp;
  LangSupport &Lang = getGlobalContext().getSwiftLangSupport();
  Lang.findInterfaceDocument(ModuleName, Args,
    [&](const InterfaceDocInfo &Info) {
      if (!Info.Error.empty()) {
        SmallString<128> Err(Info.Error);
        Resp = createErrorRequestFailed(Err.c_str());
        return;
      }

      auto Elem = RespBuilder.getDictionary();
      if (!Info.ModuleInterfaceName.empty())
        Elem.set(KeyModuleInterfaceName, Info.ModuleInterfaceName);
      if (!Info.CompilerArgs.empty())
        Elem.set(KeyCompilerArgs, Info.CompilerArgs);
      Resp = RespBuilder.createResponse();
    });

  return Resp;
}

static bool isSemanticEditorDisabled() {
  enum class SemaInfoToggle : char {
    None, Disable, Enable
  };
  static SemaInfoToggle Toggle = SemaInfoToggle::None;

  if (Toggle == SemaInfoToggle::None) {
    static std::once_flag flag;
    std::call_once(flag, []() {
      Toggle = SemaInfoToggle::Enable;

      const char *EnvOpt = ::getenv("SOURCEKIT_DELAY_SEMA_EDITOR");
      if (!EnvOpt) {
        return;
      }

      unsigned Seconds;
      if (StringRef(EnvOpt).getAsInteger(10, Seconds))
        return;

      // A crash occurred previously. Disable semantic info in the editor for
      // the given amount, to avoid repeated crashers.
      LOG_WARN_FUNC("delaying semantic editor for " << Seconds << " seconds");
      Toggle = SemaInfoToggle::Disable;
      dispatch_time_t When = dispatch_time(DISPATCH_TIME_NOW,
                                           NSEC_PER_SEC * Seconds);
      dispatch_after(When, dispatch_get_main_queue(), ^{
        Toggle = SemaInfoToggle::Enable;
      });
    });
  }

  assert(Toggle != SemaInfoToggle::None);
  return Toggle == SemaInfoToggle::Disable;
}
