blob: df04164c8d5fd9356e93930212fdf51706886d35 [file] [log] [blame]
// Copyright 2021 The Verible Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef VERIBLE_COMMON_LSP_LSP_TEXT_BUFFER_H
#define VERIBLE_COMMON_LSP_LSP_TEXT_BUFFER_H
#include <functional>
#include <memory>
#include <vector>
//
#include "absl/strings/str_cat.h"
#include "absl/strings/str_split.h"
#include "absl/strings/string_view.h"
#include "common/lsp/json-rpc-dispatcher.h"
#include "common/lsp/lsp-protocol.h"
namespace verible {
namespace lsp {
// The EditTextBuffer keeps track of the content of buffers on the client.
// It is fed initially with the full content, and from then on receives
// change events to keep in sync.
// It provides ways to pass the current content to a requestor that needs to
// process it.
class EditTextBuffer {
public:
using ContentProcessFun = std::function<void(absl::string_view)>;
explicit EditTextBuffer(absl::string_view initial_text);
EditTextBuffer(const EditTextBuffer &) = delete;
EditTextBuffer(EditTextBuffer &&) = delete;
// Requst to flatten the content call and call function "processor" that
// gets a string_view of the current state that is valid for the duration
// of the call.
void RequestContent(const ContentProcessFun &processor) const;
// Same as RequestContent() for a specific line.
void RequestLine(int line, const ContentProcessFun &processor) const;
// Apply a single LSP edit operation.
bool ApplyChange(const TextDocumentContentChangeEvent &c);
// Apply a sequence of changes.
void ApplyChanges(const std::vector<TextDocumentContentChangeEvent> &cc);
// Lines in this document.
size_t lines() const { return lines_.size(); }
// Length of document in bytes.
int64_t document_length() const { return document_length_; }
// Last global version number this buffer has edited from.
int64_t last_global_version() const { return last_global_version_; }
// Set global version; this typically will be done by the BufferCollection.
void set_last_global_version(int64_t v) { last_global_version_ = v; }
private:
// TODO: this should be unique_ptr, but assignment in the insert() command
// will not work. Needs to be formulated with something something std::move ?
using LineVector = std::vector<std::shared_ptr<std::string>>;
static LineVector GenerateLines(absl::string_view content);
void ReplaceDocument(absl::string_view content);
bool LineEdit(const TextDocumentContentChangeEvent &c, std::string *str);
bool MultiLineEdit(const TextDocumentContentChangeEvent &c);
int64_t last_global_version_ = 0;
int64_t document_length_ = 0;
LineVector lines_;
};
// A buffer collection keeps track of various open text buffers on the
// client side. Registers new ExitTextBuffers by subscribing to events
// coming from the client.
class BufferCollection {
public:
// Create buffer collection and subscribe to buffer events at the dispatcher.
explicit BufferCollection(JsonRpcDispatcher *dispatcher);
BufferCollection(const BufferCollection &) = delete;
BufferCollection(BufferCollection &&) = delete;
// Handle textDocument/didOpen event; create a new EditTextBuffer.
void didOpenEvent(const DidOpenTextDocumentParams &o);
// Handle textDocument/didChange event. Delegate changes to existing buffer.
void didChangeEvent(const DidChangeTextDocumentParams &o);
// Handle textDocument/didClose event. Forget about buffer.
void didCloseEvent(const DidCloseTextDocumentParams &o);
const EditTextBuffer *findBufferByUri(const std::string &uri) const {
auto found = buffers_.find(uri);
return found == buffers_.end() ? nullptr : found->second.get();
}
// Edits done on all buffers from all time. Allows to compare a single
// number if there is any change since last time. Good to remember to get
// only changed buffers when calling MapBuffersChangedSince()
int64_t global_version() const { return global_version_; }
using UriBufferCallback =
std::function<void(const std::string &uri, const EditTextBuffer *buffer)>;
// Set a callback that is called with each change on the buffer collection.
//
// If a new buffer has been added or received a change, the callback is
// called with the uri and a pointer to the buffer.
// If a buffer has been removed, the uri of the removed buffer
// and a nullptr will be sent.
//
// Note, there can only be one callback at a time, which is sufficient
// for the current uses; if that changes, need some Add/Remove calls.
void SetChangeListener(const UriBufferCallback &listener) {
change_listener_ = listener;
}
size_t documents_open() const { return buffers_.size(); }
private:
int64_t global_version_ = 0;
UriBufferCallback change_listener_ = nullptr;
std::unordered_map<std::string, std::unique_ptr<EditTextBuffer>> buffers_;
};
} // namespace lsp
} // namespace verible
#endif // VERIBLE_COMMON_LSP_LSP_TEXT_BUFFER_H