Summary: This patch adds support for incremental document syncing, as described in the LSP spec. The protocol specifies ranges in terms of Position (a line and a character), and our drafts are stored as plain strings. So I see two things that may not be super efficient for very large files: - Converting a Position to an offset (the positionToOffset function) requires searching for end of lines until we reach the desired line. - When we update a range, we construct a new string, which implies copying the whole document. However, for the typical size of a C++ document and the frequency of update (at which a user types), it may not be an issue. This patch aims at getting the basic feature in, and we can always improve it later if we find it's too slow. Signed-off-by: Simon Marchi <simon.marchi@ericsson.com> Reviewers: malaperle, ilya-biryukov Reviewed By: ilya-biryukov Subscribers: MaskRay, klimek, mgorny, ilya-biryukov, jkorous-apple, ioeric, cfe-commits Differential Revision: https://reviews.llvm.org/D44272 llvm-svn: 328500
108 lines
3.1 KiB
C++
108 lines
3.1 KiB
C++
//===--- DraftStore.cpp - File contents container ---------------*- C++ -*-===//
|
|
//
|
|
// The LLVM Compiler Infrastructure
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "DraftStore.h"
|
|
#include "SourceCode.h"
|
|
#include "llvm/Support/Errc.h"
|
|
|
|
using namespace clang;
|
|
using namespace clang::clangd;
|
|
|
|
llvm::Optional<std::string> DraftStore::getDraft(PathRef File) const {
|
|
std::lock_guard<std::mutex> Lock(Mutex);
|
|
|
|
auto It = Drafts.find(File);
|
|
if (It == Drafts.end())
|
|
return llvm::None;
|
|
|
|
return It->second;
|
|
}
|
|
|
|
std::vector<Path> DraftStore::getActiveFiles() const {
|
|
std::lock_guard<std::mutex> Lock(Mutex);
|
|
std::vector<Path> ResultVector;
|
|
|
|
for (auto DraftIt = Drafts.begin(); DraftIt != Drafts.end(); DraftIt++)
|
|
ResultVector.push_back(DraftIt->getKey());
|
|
|
|
return ResultVector;
|
|
}
|
|
|
|
void DraftStore::addDraft(PathRef File, StringRef Contents) {
|
|
std::lock_guard<std::mutex> Lock(Mutex);
|
|
|
|
Drafts[File] = Contents;
|
|
}
|
|
|
|
llvm::Expected<std::string> DraftStore::updateDraft(
|
|
PathRef File, llvm::ArrayRef<TextDocumentContentChangeEvent> Changes) {
|
|
std::lock_guard<std::mutex> Lock(Mutex);
|
|
|
|
auto EntryIt = Drafts.find(File);
|
|
if (EntryIt == Drafts.end()) {
|
|
return llvm::make_error<llvm::StringError>(
|
|
"Trying to do incremental update on non-added document: " + File,
|
|
llvm::errc::invalid_argument);
|
|
}
|
|
|
|
std::string Contents = EntryIt->second;
|
|
|
|
for (const TextDocumentContentChangeEvent &Change : Changes) {
|
|
if (!Change.range) {
|
|
Contents = Change.text;
|
|
continue;
|
|
}
|
|
|
|
const Position &Start = Change.range->start;
|
|
llvm::Expected<size_t> StartIndex =
|
|
positionToOffset(Contents, Start, false);
|
|
if (!StartIndex)
|
|
return StartIndex.takeError();
|
|
|
|
const Position &End = Change.range->end;
|
|
llvm::Expected<size_t> EndIndex = positionToOffset(Contents, End, false);
|
|
if (!EndIndex)
|
|
return EndIndex.takeError();
|
|
|
|
if (*EndIndex < *StartIndex)
|
|
return llvm::make_error<llvm::StringError>(
|
|
llvm::formatv(
|
|
"Range's end position ({0}) is before start position ({1})", End,
|
|
Start),
|
|
llvm::errc::invalid_argument);
|
|
|
|
if (Change.rangeLength &&
|
|
(ssize_t)(*EndIndex - *StartIndex) != *Change.rangeLength)
|
|
return llvm::make_error<llvm::StringError>(
|
|
llvm::formatv("Change's rangeLength ({0}) doesn't match the "
|
|
"computed range length ({1}).",
|
|
*Change.rangeLength, *EndIndex - *StartIndex),
|
|
llvm::errc::invalid_argument);
|
|
|
|
std::string NewContents;
|
|
NewContents.reserve(*StartIndex + Change.text.length() +
|
|
(Contents.length() - *EndIndex));
|
|
|
|
NewContents = Contents.substr(0, *StartIndex);
|
|
NewContents += Change.text;
|
|
NewContents += Contents.substr(*EndIndex);
|
|
|
|
Contents = std::move(NewContents);
|
|
}
|
|
|
|
EntryIt->second = Contents;
|
|
return Contents;
|
|
}
|
|
|
|
void DraftStore::removeDraft(PathRef File) {
|
|
std::lock_guard<std::mutex> Lock(Mutex);
|
|
|
|
Drafts.erase(File);
|
|
}
|