| // 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 |