#include "Buffer.h" #include #include "System.h" #include "File.h" #include "TextLoader.h" #include #include Buffer::Buffer() { common_pre_load_initialization(); load_empty_buffer(); common_post_load_initialization(); } Buffer::Buffer(const char * filename) { common_pre_load_initialization(); if (!load_from_file(filename)) { load_empty_buffer(); } common_post_load_initialization(); } Buffer::Buffer(const uint8_t * data, size_t data_length) { common_pre_load_initialization(); if (!load_from_memory(data, data_length)) { load_empty_buffer(); } common_post_load_initialization(); } void Buffer::common_pre_load_initialization() { m_tabstop = 4u; m_change_buffer = std::make_shared(); m_operation_level = 0; m_current_change_operation_index = INVALID_CHANGE_OPERATION_INDEX; m_encoding = Encoding::UTF_8; } void Buffer::common_post_load_initialization() { m_eof_iterator = add_iterator(); while (m_eof_iterator->valid()) { m_eof_iterator->go_forward(); } } void Buffer::load_empty_buffer() { m_gap_buffer = std::make_shared(); m_eol_at_eof = true; m_line_endings = LineEndings::LF; } bool Buffer::load_from_file(const char * filename) { File file; if (!file.open(filename)) { return false; } size_t file_size = file.get_size(); if (file_size < 0) { return false; } size_t buffer_size = ((unsigned long)file_size + System::page_size + (64u * 1024u)) & System::page_base_mask; uint8_t * buffer = (uint8_t *)System::alloc_pages(buffer_size >> System::page_size_log); if (buffer == NULL) { return false; } if (!file.read(buffer, file_size)) { System::free_pages(buffer, buffer_size >> System::page_size_log); return false; } load_text_in_buffer(buffer, buffer_size, file_size); m_filename = std::make_shared(filename); return true; } bool Buffer::load_from_memory(const uint8_t * data, size_t data_length) { size_t buffer_size = ((unsigned long)data_length + 2u * System::page_size) & System::page_base_mask; uint8_t * buffer = (uint8_t *)System::alloc_pages(buffer_size >> System::page_size_log); if (buffer == NULL) { return false; } memcpy(buffer, data, data_length); load_text_in_buffer(buffer, buffer_size, data_length); return true; } void Buffer::load_text_in_buffer(uint8_t * buffer, size_t buffer_size, size_t data_length) { TextLoader text_loader; size_t loaded_size; text_loader.load_buffer(buffer, data_length, &loaded_size); m_gap_buffer = std::make_shared(buffer, buffer_size, loaded_size); m_encoding = text_loader.get_encoding(); m_eol_at_eof = text_loader.get_eol_at_eof(); m_line_endings = text_loader.get_line_endings(); } bool Buffer::write_to_file(const char * filename) { File file; if (!file.open(filename, true)) { return false; } m_gap_buffer->compact(); Iterator start_of_line(this); size_t bytes_written = 0u; while (start_of_line.valid()) { Iterator iterator = start_of_line; iterator.go_end_of_line(true); size_t len = iterator.address() - start_of_line.address(); if (len > 0u) { if (!file.write(start_of_line.address(), len)) { return false; } bytes_written += len; } bool moved_down = iterator.go_next_line(); if (moved_down || m_eol_at_eof) { if (!file.write(LineEndings::spans[m_line_endings])) { return false; } bytes_written += LineEndings::spans[m_line_endings].length; } if (!moved_down) { break; } start_of_line = iterator; } return true; } void Buffer::enter_insert_mode() { m_insert_mode = true; push_operation(); } void Buffer::exit_insert_mode() { m_insert_mode = false; pop_operation(); } void Buffer::insert_code_point(const Buffer::Iterator & position, uint32_t code_point) { push_operation(); uint8_t encoded[Encoding::MAX_CODE_POINT_SIZE * 2]; uint8_t bytes = Encoding::encode(code_point, m_encoding, encoded); if ((size() == 0u) && (code_point != '\n')) { bytes += Encoding::encode('\n', m_encoding, &encoded[bytes]); } insert_data(position.offset(), encoded, bytes, true); pop_operation(); } void Buffer::insert_data(size_t offset, const uint8_t data[], size_t length, bool do_record_change) { m_gap_buffer->insert(offset, data, length); if (do_record_change) { record_change(offset, length, true); } warp_iterators_after_insert(offset, length); post_warp_cursors(); } void Buffer::erase_code_point(const Buffer::Iterator & position) { if (position.valid()) { push_operation(); erase_data(position.offset(), Encoding::num_bytes_in_code_point(m_encoding, position.address()), true); pop_operation(); } } void Buffer::erase_range(const Buffer::Iterator & start, const Buffer::Iterator & end) { if (start.valid() && (start < end)) { push_operation(); size_t start_offset = start.offset(); size_t end_offset = end.valid() ? end.offset() : m_gap_buffer->size(); erase_data(start_offset, end_offset - start_offset, true); pop_operation(); } } void Buffer::erase_data(size_t offset, size_t length, bool do_record_change) { warp_iterators_before_delete(offset, length); if (do_record_change) { record_change(offset, length, false); } m_gap_buffer->erase(offset, length); post_warp_cursors(); } size_t Buffer::lines_in_data(size_t offset, size_t length) { size_t lines = 0u; for (size_t i = 0u; i < length; ) { uint8_t bytes; uint32_t c = Encoding::decode(m_encoding, m_gap_buffer->address(offset + i), &bytes); if (c == '\n') { lines++; } i += bytes; } return lines; } void Buffer::post_warp_cursors() { if (size() > 0u) { for (auto cursor : m_cursors) { if (*cursor == *m_eof_iterator) { cursor->go_back(); } if ((!insert_mode()) && (**cursor == '\n')) { cursor->go_left_in_line(); } } } } void Buffer::warp_iterators_after_insert(size_t offset, size_t length) { size_t lines = lines_in_data(offset, length); for (auto iterator : m_iterators) { if (iterator->offset() >= offset) { iterator->warp(length, lines); } } } void Buffer::warp_iterators_before_delete(size_t offset, size_t length) { size_t lines = lines_in_data(offset, length); for (auto iterator : m_iterators) { /* Move any iterators within the chunk to be deleted to after it. */ while ((iterator->offset() >= offset) && (iterator->offset() < (offset + length))) { iterator->go_forward(); if (!iterator->valid()) { break; } } if (iterator->offset() >= offset) { iterator->warp(-(ssize_t)length, -(ssize_t)lines); } } } void Buffer::pop_operation() { if (m_operation_level > 0) { m_operation_level--; if (m_operation_level == 0) { save_current_operation(); } } } void Buffer::record_change(size_t offset, size_t length, bool insert) { if (!m_current_change_operation) { m_current_change_operation = std::make_shared(); m_current_change_operation->parent = m_current_change_operation_index; } 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 && ((offset + length) >= cu.buffer_position) && (offset <= (cu.buffer_position + cu.length))) { /* The new insertion can extend the previous insertion change unit. */ size_t change_buffer_offset = cu.change_buffer_offset + offset - cu.buffer_position; m_gap_buffer->copy_to(offset, length, *m_change_buffer, change_buffer_offset); cu.length += length; cu.change_buffer_offset = std::min(cu.change_buffer_offset, change_buffer_offset); cu.buffer_position = std::min(cu.buffer_position, offset); return; } else if ((!insert) && (!cu.insert) && ((offset == cu.buffer_position) || ((offset + length) == cu.buffer_position))) { /* The new deletion can extend the previous deletion change unit. */ if (offset == cu.buffer_position) { /* The new deletion immediately follows the previous. */ m_gap_buffer->copy_to(offset, length, *m_change_buffer, cu.change_buffer_offset + cu.length); } else { /* The new deletion immediately precedes the previous. */ m_gap_buffer->copy_to(offset, length, *m_change_buffer, cu.change_buffer_offset); cu.buffer_position = offset; } cu.length += length; return; } else if ((!insert) && cu.insert && (offset >= cu.buffer_position) && ((offset + length) <= (cu.buffer_position + cu.length))) { /* The deletion is removing from the previous insertion change unit. */ m_change_buffer->erase(cu.change_buffer_offset + (offset - 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_gap_buffer->copy_to(offset, length, *m_change_buffer, change_buffer_offset); ChangeUnit cu; cu.change_buffer_offset = change_buffer_offset; cu.length = length; cu.buffer_position = offset; cu.insert = insert; m_current_change_operation->changes.push_back(cu); } void Buffer::save_current_operation() { if (m_current_change_operation) { if (m_current_change_operation->changes.size() > 0u) { size_t new_change_operation_index = m_change_operations.size(); if (m_current_change_operation_index < new_change_operation_index) { m_change_operations[m_current_change_operation_index]->children.push_back(new_change_operation_index); } m_change_operations.push_back(m_current_change_operation); m_current_change_operation_index = new_change_operation_index; } m_current_change_operation = nullptr; } } void Buffer::apply_change_operation(const ChangeOperation & change_operation, bool forward) { if (forward) { for (auto change_unit : change_operation.changes) { apply_change_unit(change_unit, forward); } } else { for (auto i = change_operation.changes.rbegin(); i != change_operation.changes.rend(); i++) { apply_change_unit(*i, forward); } } } void Buffer::apply_change_unit(const ChangeUnit & change_unit, bool forward) { if (forward == change_unit.insert) { m_change_buffer->compact(); insert_data(change_unit.buffer_position, m_change_buffer->address(change_unit.change_buffer_offset), change_unit.length, false); } else { erase_data(change_unit.buffer_position, change_unit.length, false); } } void Buffer::undo() { if (m_current_change_operation_index != INVALID_CHANGE_OPERATION_INDEX) { apply_change_operation(*m_change_operations[m_current_change_operation_index], false); m_current_change_operation_index = m_change_operations[m_current_change_operation_index]->parent; } } void Buffer::redo() { size_t child_index = INVALID_CHANGE_OPERATION_INDEX; if (m_current_change_operation_index == INVALID_CHANGE_OPERATION_INDEX) { if (m_change_operations.size() > 0u) { child_index = 0u; } } else if (m_change_operations[m_current_change_operation_index]->children.size() > 0u) { /* TODO: support redoing a child of the undo tree other than the first */ child_index = *m_change_operations[m_current_change_operation_index]->children.begin(); } if (child_index != INVALID_CHANGE_OPERATION_INDEX) { apply_change_operation(*m_change_operations[child_index], true); m_current_change_operation_index = child_index; } } void Buffer::clear() { m_gap_buffer->clear(); for (auto iterator : m_iterators) { auto b = begin(); *iterator = b; } } EncodedString Buffer::get_string() { m_gap_buffer->compact(); return EncodedString(m_gap_buffer->address(0u), m_gap_buffer->size(), m_encoding); }