diff --git a/src/core/Buffer.cc b/src/core/Buffer.cc index e13de1c..bb0c6d6 100644 --- a/src/core/Buffer.cc +++ b/src/core/Buffer.cc @@ -38,6 +38,7 @@ void Buffer::common_initialization() { m_eof_iterator->go_forward(); } + m_change_buffer = std::make_shared(); } void Buffer::load_empty_buffer() @@ -175,6 +176,7 @@ void Buffer::insert_code_point(const Buffer::Iterator & position, uint32_t code_ uint8_t encoded[Encoding::MAX_CODE_POINT_SIZE]; uint8_t bytes = Encoding::encode(code_point, m_encoding, encoded); m_gap_buffer->insert(local_position.offset(), encoded, bytes); + record_change(encoded, bytes, local_position.offset(), true); ssize_t lines = (code_point == '\n') ? 1 : 0; if (adjust_iterators) { @@ -196,6 +198,7 @@ void Buffer::erase_code_point(const Buffer::Iterator & position) Buffer::Iterator local_position = position; uint8_t bytes = 0u; uint32_t code_point = Encoding::decode(m_encoding, local_position.address(), &bytes); + record_change(local_position.address(), bytes, local_position.offset(), false); m_gap_buffer->erase(local_position.offset(), bytes); ssize_t lines = (code_point == '\n') ? -1 : 0; for (auto iterator : m_iterators) @@ -207,3 +210,72 @@ void Buffer::erase_code_point(const Buffer::Iterator & position) } } } + +void Buffer::record_change(uint8_t data[], size_t length, size_t buffer_position, bool insert) +{ + if (!m_current_change_operation) + { + m_current_change_operation = std::make_shared(); + } + if (m_current_change_operation->changes.size() > 0) + { + /* There is a previous change unit in progress. See if it can be + * modified to encompass the new change. */ + ChangeUnit & cu = *m_current_change_operation->changes.rbegin(); + if (insert && cu.insert && + ((buffer_position + length) >= cu.buffer_position) && + (buffer_position <= (cu.buffer_position + cu.length))) + { + /* The new insertion can extend the previous insertion change unit. */ + size_t change_buffer_offset = cu.change_buffer_offset + buffer_position - cu.buffer_position; + m_change_buffer->insert(change_buffer_offset, data, length); + cu.length += length; + cu.change_buffer_offset = std::min(cu.change_buffer_offset, change_buffer_offset); + cu.buffer_position = std::min(cu.buffer_position, buffer_position); + return; + } + else if ((!insert) && (!cu.insert) && + ((buffer_position == cu.buffer_position) || + ((buffer_position + length) == cu.buffer_position))) + { + /* The new deletion can extend the previous deletion change unit. */ + if (buffer_position == cu.buffer_position) + { + /* The new deletion immediately follows the previous. */ + m_change_buffer->insert(cu.change_buffer_offset + cu.length, data, length); + } + else + { + /* The new deletion immediately precedes the previous. */ + m_change_buffer->insert(cu.change_buffer_offset, data, length); + } + cu.length += length; + return; + } + else if ((!insert) && cu.insert && + (buffer_position >= cu.buffer_position) && + ((buffer_position + length) <= (cu.buffer_position + cu.length))) + { + /* The deletion is removing from the previous insertion change unit. */ + m_change_buffer->erase(cu.change_buffer_offset + (buffer_position - cu.buffer_position), length); + if (cu.length <= length) + { + m_current_change_operation->changes.erase(--m_current_change_operation->changes.end()); + } + else + { + cu.length -= length; + } + return; + } + } + /* Start a new change unit. */ + size_t change_buffer_offset = m_change_buffer->size(); + m_change_buffer->insert(change_buffer_offset, data, length); + ChangeUnit cu; + cu.change_buffer_offset = change_buffer_offset; + cu.length = length; + cu.buffer_position = buffer_position; + cu.insert = insert; + m_current_change_operation->changes.push_back(cu); +} diff --git a/src/core/Buffer.h b/src/core/Buffer.h index 8e9bde8..180c54a 100644 --- a/src/core/Buffer.h +++ b/src/core/Buffer.h @@ -7,6 +7,7 @@ #include "Encoding.h" #include "GapBuffer.h" #include +#include "ChangeOperation.h" class Buffer { @@ -121,18 +122,21 @@ protected: bool m_eol_at_eof; LineEndings::Type m_line_endings; std::shared_ptr m_gap_buffer; + std::shared_ptr m_change_buffer; Encoding::Type m_encoding; uint8_t m_tabstop; bool m_insert_mode; std::list> m_iterators; std::shared_ptr m_eof_iterator; std::string m_filename; + std::shared_ptr m_current_change_operation; void common_initialization(); bool load_from_file(const char * filename); void load_empty_buffer(); bool load_from_memory(const uint8_t * data, size_t data_length); void load_text_in_buffer(uint8_t * buffer, size_t buffer_size, size_t data_length); + void record_change(uint8_t data[], size_t length, size_t buffer_position, bool insert); }; #endif diff --git a/src/core/ChangeOperation.h b/src/core/ChangeOperation.h new file mode 100644 index 0000000..a177b20 --- /dev/null +++ b/src/core/ChangeOperation.h @@ -0,0 +1,15 @@ +#ifndef CHANGEOPERATION_H +#define CHANGEOPERATION_H + +#include "ChangeUnit.h" +#include +#include + +class ChangeOperation +{ +public: + std::list changes; + std::list children; +}; + +#endif diff --git a/src/core/ChangeUnit.h b/src/core/ChangeUnit.h new file mode 100644 index 0000000..4840360 --- /dev/null +++ b/src/core/ChangeUnit.h @@ -0,0 +1,15 @@ +#ifndef CHANGEUNIT_H +#define CHANGEUNIT_H + +#include + +class ChangeUnit +{ +public: + size_t change_buffer_offset; + size_t length; + size_t buffer_position; + bool insert; +}; + +#endif