remove PieceTable
This commit is contained in:
parent
65468bd2e5
commit
fbca1f995a
@ -7,13 +7,11 @@
|
||||
|
||||
Buffer::Buffer()
|
||||
{
|
||||
piece_table = std::make_shared<PieceTable>();
|
||||
m_file_buffer = nullptr;
|
||||
m_file_buffer_size = 0u;
|
||||
m_eol_at_eof = true;
|
||||
m_line_endings = LineEndings::LF;
|
||||
m_encoding = Encoding::UTF_8;
|
||||
piece_table->encoding = m_encoding;
|
||||
}
|
||||
|
||||
Buffer::~Buffer()
|
||||
@ -38,7 +36,7 @@ bool Buffer::load_from_file(const char * filename)
|
||||
return false;
|
||||
}
|
||||
|
||||
m_file_buffer_size = ((unsigned long)file_size + System::page_size) & System::page_base_mask;
|
||||
m_file_buffer_size = ((unsigned long)file_size + 2u * System::page_size) & System::page_base_mask;
|
||||
m_file_buffer = (uint8_t *)System::alloc_pages(m_file_buffer_size >> System::page_size_log);
|
||||
if (m_file_buffer == NULL)
|
||||
{
|
||||
@ -52,22 +50,12 @@ bool Buffer::load_from_file(const char * filename)
|
||||
}
|
||||
|
||||
TextLoader text_loader;
|
||||
text_loader.load_buffer(m_file_buffer, file_size);
|
||||
|
||||
piece_table = std::make_shared<PieceTable>();
|
||||
for (auto it = text_loader.begin(); it != text_loader.end(); it++)
|
||||
{
|
||||
auto next = it;
|
||||
next++;
|
||||
bool eol = next != text_loader.end();
|
||||
it->start[it->length] = PieceTable::INTERNAL_EOL;
|
||||
piece_table->append_initial_line_piece(it->start, it->length + 1u, eol);
|
||||
}
|
||||
size_t loaded_size;
|
||||
text_loader.load_buffer(m_file_buffer, file_size, &loaded_size);
|
||||
|
||||
m_eol_at_eof = text_loader.get_eol_at_eof();
|
||||
m_line_endings = text_loader.get_line_endings();
|
||||
m_encoding = text_loader.get_encoding();
|
||||
piece_table->encoding = m_encoding;
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -80,31 +68,7 @@ bool Buffer::write_to_file(const char * filename)
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t bytes_written = 0u;
|
||||
for (auto piece = piece_table->start_piece->next;
|
||||
piece != piece_table->end_piece;
|
||||
piece = piece->next)
|
||||
{
|
||||
uint32_t length = piece->length;
|
||||
if (piece->eol())
|
||||
{
|
||||
length--;
|
||||
}
|
||||
if (!file.write(piece->start, length))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
bytes_written += length;
|
||||
if (((piece->next != piece_table->end_piece) && piece->eol()) ||
|
||||
(m_eol_at_eof && bytes_written > 0u))
|
||||
{
|
||||
if (!file.write(LineEndings::spans[m_line_endings]))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
bytes_written += LineEndings::spans[m_line_endings].length;
|
||||
}
|
||||
}
|
||||
/* TODO */
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -3,15 +3,12 @@
|
||||
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include "PieceTable.h"
|
||||
#include "LineEndings.h"
|
||||
#include "Encoding.h"
|
||||
|
||||
class Buffer
|
||||
{
|
||||
public:
|
||||
std::shared_ptr<PieceTable> piece_table;
|
||||
|
||||
Buffer();
|
||||
~Buffer();
|
||||
bool load_from_file(const char * filename);
|
||||
|
@ -1,610 +0,0 @@
|
||||
#include "PieceTable.h"
|
||||
|
||||
PieceTable::PieceTable()
|
||||
{
|
||||
m_piece_index = 0u;
|
||||
start_piece = add_piece();
|
||||
end_piece = add_piece();
|
||||
start_piece->length = 0u;
|
||||
end_piece->length = 0u;
|
||||
start_piece->next = end_piece;
|
||||
end_piece->prev = start_piece;
|
||||
tabstop = 4u;
|
||||
m_append_buffer_index = 0u;
|
||||
m_changes_index = 0u;
|
||||
}
|
||||
|
||||
void PieceTable::append_initial_line_piece(uint8_t * start, uint32_t length, bool eol)
|
||||
{
|
||||
Piece * piece = add_piece();
|
||||
piece->prev = end_piece->prev;
|
||||
piece->next = end_piece;
|
||||
piece->start = start;
|
||||
piece->length = length;
|
||||
end_piece->prev->next = piece;
|
||||
end_piece->prev = piece;
|
||||
m_num_lines++;
|
||||
}
|
||||
|
||||
std::shared_ptr<PieceTable::Cursor> PieceTable::add_cursor()
|
||||
{
|
||||
auto cursor = std::make_shared<Cursor>(this);
|
||||
|
||||
m_cursors.push_back(cursor);
|
||||
|
||||
return cursor;
|
||||
}
|
||||
|
||||
PieceTable::Iterator::Iterator(PieceTable * pt)
|
||||
{
|
||||
piece_table = pt;
|
||||
piece = pt->start_piece->next;
|
||||
while ((piece->length == 0u) && (piece != piece_table->end_piece))
|
||||
{
|
||||
piece = piece->next;
|
||||
}
|
||||
offset = 0u;
|
||||
}
|
||||
|
||||
uint8_t PieceTable::Iterator::num_bytes_in_code_point() const
|
||||
{
|
||||
if (offset >= piece->length)
|
||||
{
|
||||
return 0u;
|
||||
}
|
||||
else
|
||||
{
|
||||
return Encoding::num_bytes_in_code_point(piece_table->encoding, &piece->start[offset]);
|
||||
}
|
||||
}
|
||||
|
||||
void PieceTable::Iterator::go_next_position()
|
||||
{
|
||||
if (valid())
|
||||
{
|
||||
uint8_t code_point_size = num_bytes_in_code_point();
|
||||
if ((offset + code_point_size) >= piece->length)
|
||||
{
|
||||
do
|
||||
{
|
||||
piece = piece->next;
|
||||
} while ((piece != piece_table->end_piece) &&
|
||||
(piece->length == 0u));
|
||||
offset = 0u;
|
||||
return;
|
||||
}
|
||||
offset += code_point_size;
|
||||
}
|
||||
}
|
||||
|
||||
void PieceTable::Iterator::go_prev_position()
|
||||
{
|
||||
if (piece != piece_table->start_piece)
|
||||
{
|
||||
if (offset > 0)
|
||||
{
|
||||
offset = Encoding::beginning_of_code_point(piece_table->encoding, &piece->start[offset - 1u]) - piece->start;
|
||||
}
|
||||
else
|
||||
{
|
||||
do
|
||||
{
|
||||
piece = piece->prev;
|
||||
} while ((piece != piece_table->start_piece) &&
|
||||
(piece->length == 0u));
|
||||
if (piece->length > 0u)
|
||||
{
|
||||
offset = Encoding::beginning_of_code_point(piece_table->encoding, &piece->start[piece->length - 1u]) - piece->start;
|
||||
}
|
||||
else
|
||||
{
|
||||
offset = 0u;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PieceTable::Iterator::go_end_of_piece()
|
||||
{
|
||||
if (piece->length > 0u)
|
||||
{
|
||||
offset = Encoding::beginning_of_code_point(piece_table->encoding, &piece->start[piece->length - 1u]) - piece->start;
|
||||
}
|
||||
else
|
||||
{
|
||||
offset = 0u;
|
||||
}
|
||||
}
|
||||
|
||||
void PieceTable::Iterator::go_next_piece()
|
||||
{
|
||||
if (piece != piece_table->end_piece)
|
||||
{
|
||||
do
|
||||
{
|
||||
piece = piece->next;
|
||||
} while ((piece != piece_table->end_piece) &&
|
||||
(piece->length == 0u));
|
||||
offset = 0u;
|
||||
}
|
||||
}
|
||||
|
||||
void PieceTable::Iterator::go_prev_piece()
|
||||
{
|
||||
if (piece != piece_table->start_piece)
|
||||
{
|
||||
do
|
||||
{
|
||||
piece = piece->prev;
|
||||
} while ((piece != piece_table->start_piece) &&
|
||||
(piece->length == 0u));
|
||||
offset = 0u;
|
||||
}
|
||||
}
|
||||
|
||||
PieceTable::Cursor::Cursor(PieceTable * pt) : iterator(pt)
|
||||
{
|
||||
line = 0u;
|
||||
init_column();
|
||||
}
|
||||
|
||||
void PieceTable::Cursor::go_start_of_line()
|
||||
{
|
||||
Iterator last_iterator = iterator;
|
||||
for (;;)
|
||||
{
|
||||
iterator.go_prev_piece();
|
||||
if (iterator.piece_eol() || !iterator.valid())
|
||||
{
|
||||
break;
|
||||
}
|
||||
last_iterator = iterator;
|
||||
}
|
||||
iterator = last_iterator;
|
||||
iterator.offset = 0u;
|
||||
init_column();
|
||||
}
|
||||
|
||||
bool PieceTable::Cursor::check_go_start_of_line()
|
||||
{
|
||||
uint32_t current_line = line;
|
||||
uint32_t current_column = column;
|
||||
go_start_of_line();
|
||||
return (current_line != line) || (current_column != column);
|
||||
}
|
||||
|
||||
void PieceTable::Cursor::go_end_of_line(bool allow_on_eol)
|
||||
{
|
||||
if (!iterator.valid())
|
||||
{
|
||||
return;
|
||||
}
|
||||
for (;;)
|
||||
{
|
||||
if (iterator.piece_eol())
|
||||
{
|
||||
break;
|
||||
}
|
||||
Iterator move_iterator = iterator;
|
||||
move_iterator.go_next_piece();
|
||||
if (move_iterator.valid())
|
||||
{
|
||||
iterator = move_iterator;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
iterator.go_end_of_piece();
|
||||
calculate_column();
|
||||
if (!allow_on_eol)
|
||||
{
|
||||
go_left(1);
|
||||
}
|
||||
}
|
||||
|
||||
bool PieceTable::Cursor::check_go_end_of_line(bool allow_on_eol)
|
||||
{
|
||||
uint32_t current_line = line;
|
||||
uint32_t current_column = column;
|
||||
go_end_of_line(allow_on_eol);
|
||||
return (current_line != line) || (current_column != column);
|
||||
}
|
||||
|
||||
void PieceTable::Cursor::go_up(int n, uint32_t desired_column)
|
||||
{
|
||||
if (!iterator.valid())
|
||||
return;
|
||||
Piece * p = prev_line();
|
||||
if (p == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
iterator.piece = p;
|
||||
iterator.offset = 0u;
|
||||
line--;
|
||||
init_column();
|
||||
forward_to_column(desired_column);
|
||||
}
|
||||
|
||||
bool PieceTable::Cursor::check_go_up(int n, uint32_t desired_column)
|
||||
{
|
||||
uint32_t current_line = line;
|
||||
uint32_t current_column = column;
|
||||
go_up(n, desired_column);
|
||||
return (current_line != line) || (current_column != column);
|
||||
}
|
||||
|
||||
void PieceTable::Cursor::go_down(int n, uint32_t desired_column)
|
||||
{
|
||||
if (!iterator.valid())
|
||||
return;
|
||||
Piece * p = next_line();
|
||||
if (p == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
iterator.piece = p;
|
||||
iterator.offset = 0u;
|
||||
line++;
|
||||
init_column();
|
||||
forward_to_column(desired_column);
|
||||
}
|
||||
|
||||
bool PieceTable::Cursor::check_go_down(int n, uint32_t desired_column)
|
||||
{
|
||||
uint32_t current_line = line;
|
||||
uint32_t current_column = column;
|
||||
go_down(n, desired_column);
|
||||
return (current_line != line) || (current_column != column);
|
||||
}
|
||||
|
||||
void PieceTable::Cursor::go_left(int n)
|
||||
{
|
||||
if (!iterator.valid())
|
||||
return;
|
||||
if (is_start_of_line())
|
||||
{
|
||||
return;
|
||||
}
|
||||
uint32_t chr = *iterator;
|
||||
iterator.go_prev_position();
|
||||
if (chr == '\t')
|
||||
{
|
||||
calculate_column();
|
||||
}
|
||||
else
|
||||
{
|
||||
/* TODO: handle multi-column characters */
|
||||
column--;
|
||||
}
|
||||
}
|
||||
|
||||
bool PieceTable::Cursor::check_go_left(int n)
|
||||
{
|
||||
uint32_t current_line = line;
|
||||
uint32_t current_column = column;
|
||||
go_left(n);
|
||||
return (current_line != line) || (current_column != column);
|
||||
}
|
||||
|
||||
void PieceTable::Cursor::go_right(int n, bool allow_on_eol)
|
||||
{
|
||||
if (!iterator.valid())
|
||||
return;
|
||||
if (is_end_of_line())
|
||||
{
|
||||
return;
|
||||
}
|
||||
Iterator iterator_2 = iterator;
|
||||
iterator.go_next_position();
|
||||
if ((!allow_on_eol) && is_end_of_line())
|
||||
{
|
||||
iterator = iterator_2;
|
||||
return;
|
||||
}
|
||||
uint32_t chr = *iterator;
|
||||
if (chr == '\t')
|
||||
{
|
||||
uint8_t tabstop = iterator.piece_table->tabstop;
|
||||
column += tabstop - (column + 1u) % tabstop;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* TODO: handle multi-column characters */
|
||||
column++;
|
||||
}
|
||||
}
|
||||
|
||||
bool PieceTable::Cursor::check_go_right(int n, bool allow_on_eol)
|
||||
{
|
||||
uint32_t current_line = line;
|
||||
uint32_t current_column = column;
|
||||
go_right(n, allow_on_eol);
|
||||
return (current_line != line) || (current_column != column);
|
||||
}
|
||||
|
||||
void PieceTable::Cursor::warp_to_inserted_piece(Piece * piece)
|
||||
{
|
||||
iterator.piece = piece;
|
||||
iterator.offset = 0u;
|
||||
iterator.go_end_of_piece();
|
||||
calculate_column();
|
||||
}
|
||||
|
||||
void PieceTable::Cursor::warp_to_beginning_of_piece(Piece * piece)
|
||||
{
|
||||
iterator.piece = piece;
|
||||
iterator.offset = 0u;
|
||||
calculate_column();
|
||||
}
|
||||
|
||||
bool PieceTable::Cursor::is_start_of_line()
|
||||
{
|
||||
if (!iterator.valid() || (iterator.offset > 0u))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
Iterator iterator2 = iterator;
|
||||
iterator2.go_prev_piece();
|
||||
return iterator2.piece_eol() || !iterator2.valid();
|
||||
}
|
||||
|
||||
bool PieceTable::Cursor::is_end_of_line()
|
||||
{
|
||||
return *iterator == INTERNAL_EOL;
|
||||
}
|
||||
|
||||
PieceTable::Piece * PieceTable::Cursor::prev_line() const
|
||||
{
|
||||
if (!iterator.valid())
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
Cursor cursor2 = *this;
|
||||
cursor2.go_start_of_line();
|
||||
cursor2.iterator.go_prev_position();
|
||||
if (!cursor2.iterator.valid())
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
cursor2.go_start_of_line();
|
||||
return cursor2.iterator.piece;
|
||||
}
|
||||
|
||||
PieceTable::Piece * PieceTable::Cursor::next_line() const
|
||||
{
|
||||
if (!iterator.valid())
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
Cursor cursor2 = *this;
|
||||
cursor2.go_end_of_line(true);
|
||||
cursor2.iterator.go_next_position();
|
||||
if (!cursor2.iterator.valid())
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
return cursor2.iterator.piece;
|
||||
}
|
||||
|
||||
void PieceTable::Cursor::init_column()
|
||||
{
|
||||
uint32_t chr = *iterator;
|
||||
/* TODO: handle multi-column characters */
|
||||
column = (chr == '\t') ? iterator.piece_table->tabstop - 1u : 0u;
|
||||
}
|
||||
|
||||
void PieceTable::Cursor::calculate_column()
|
||||
{
|
||||
Cursor tmp_cursor = *this;
|
||||
tmp_cursor.go_start_of_line();
|
||||
while ((tmp_cursor.iterator.piece != iterator.piece) ||
|
||||
(tmp_cursor.iterator.offset < iterator.offset))
|
||||
{
|
||||
tmp_cursor.go_right(1, true);
|
||||
}
|
||||
column = tmp_cursor.column;
|
||||
}
|
||||
|
||||
void PieceTable::Cursor::forward_to_column(uint32_t c)
|
||||
{
|
||||
int32_t diff = abs((int32_t)(c - column));
|
||||
if (diff == 0)
|
||||
return;
|
||||
while (check_go_right(1, false))
|
||||
{
|
||||
int32_t new_diff = abs((int32_t)(c - column));
|
||||
if (new_diff > diff)
|
||||
{
|
||||
go_left(1);
|
||||
return;
|
||||
}
|
||||
diff = new_diff;
|
||||
}
|
||||
}
|
||||
|
||||
void PieceTable::begin_insert(Cursor & cursor, bool before)
|
||||
{
|
||||
Piece * piece = cursor.iterator.piece;
|
||||
uint32_t offset = cursor.iterator.offset;
|
||||
if (!before && cursor.iterator.has_char() && (*cursor != INTERNAL_EOL))
|
||||
{
|
||||
offset += cursor.iterator.num_bytes_in_code_point();
|
||||
}
|
||||
Change * change = add_change();
|
||||
m_inserting_piece = add_piece();
|
||||
m_inserting_piece->start = &m_append_buffer[m_append_buffer_index];
|
||||
m_inserting_piece->length = 0u;
|
||||
if (offset == 0u)
|
||||
{
|
||||
/* Insert happens at piece boundary between piece->prev and piece. */
|
||||
change->before = piece->prev;
|
||||
change->after = piece;
|
||||
}
|
||||
else if (offset >= piece->length)
|
||||
{
|
||||
/* Insert happens at piece boundary between piece and piece->next. */
|
||||
change->before = piece;
|
||||
change->after = piece->next;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Insert happens in the middle of a current piece. The piece must
|
||||
* be split up. */
|
||||
Piece * new_piece_1 = add_piece();
|
||||
new_piece_1->start = piece->start;
|
||||
new_piece_1->length = offset;
|
||||
|
||||
Piece * new_piece_2 = add_piece();
|
||||
new_piece_2->start = piece->start + offset;
|
||||
new_piece_2->length = piece->length - offset;
|
||||
|
||||
new_piece_1->prev = piece->prev;
|
||||
new_piece_1->next = new_piece_2;
|
||||
new_piece_2->prev = new_piece_1;
|
||||
new_piece_2->next = piece->next;
|
||||
|
||||
Change * change2 = add_change();
|
||||
change2->before = piece->prev;
|
||||
change2->after = piece->next;
|
||||
change2->chains[0][0] = piece;
|
||||
change2->chains[0][1] = nullptr;
|
||||
change2->chains[1][0] = new_piece_1;
|
||||
change2->chains[1][1] = new_piece_2;
|
||||
|
||||
apply_change(change2, 1u);
|
||||
|
||||
change->before = new_piece_1;
|
||||
change->after = new_piece_2;
|
||||
}
|
||||
|
||||
change->chains[0][0] = nullptr;
|
||||
change->chains[0][1] = nullptr;
|
||||
change->chains[1][0] = m_inserting_piece;
|
||||
change->chains[1][1] = nullptr;
|
||||
|
||||
apply_change(change, 1u);
|
||||
cursor.warp_to_beginning_of_piece(change->after);
|
||||
}
|
||||
|
||||
void PieceTable::insert_code_point(uint32_t code_point)
|
||||
{
|
||||
if (in_insert_mode())
|
||||
{
|
||||
/* TODO: worry about append buffer page boundaries */
|
||||
uint8_t bytes = Encoding::encode(code_point, encoding, &m_append_buffer[m_append_buffer_index]);
|
||||
m_inserting_piece->length += bytes;
|
||||
m_append_buffer_index += bytes;
|
||||
|
||||
for (auto cursor : m_cursors)
|
||||
{
|
||||
cursor->calculate_column();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PieceTable::end_insert()
|
||||
{
|
||||
m_inserting_piece = nullptr;
|
||||
}
|
||||
|
||||
void PieceTable::insertion_test(Cursor & c)
|
||||
{
|
||||
Piece * piece = add_piece();
|
||||
piece->start = &m_append_buffer[m_append_buffer_index];
|
||||
piece->length = 3u;
|
||||
m_append_buffer[m_append_buffer_index++] = 'x';
|
||||
m_append_buffer[m_append_buffer_index++] = 'y';
|
||||
m_append_buffer[m_append_buffer_index++] = 'z';
|
||||
|
||||
Change * change = add_change();
|
||||
Change * change2 = nullptr;
|
||||
|
||||
if (c.iterator.valid())
|
||||
{
|
||||
change->before = c.iterator.piece->prev;
|
||||
change->after = c.iterator.piece->next;
|
||||
change->chains[0][0] = c.iterator.piece;
|
||||
change->chains[0][1] = nullptr;
|
||||
if (c.iterator.offset > 0u)
|
||||
{
|
||||
Piece * piece2 = add_piece();
|
||||
piece2->start = c.iterator.piece->start;
|
||||
piece2->length = c.iterator.offset;
|
||||
|
||||
Piece * piece3 = add_piece();
|
||||
piece3->start = c.iterator.piece->start + c.iterator.offset;
|
||||
piece3->length = c.iterator.piece->length - c.iterator.offset;
|
||||
|
||||
piece2->next = piece;
|
||||
piece->prev = piece2;
|
||||
piece->next = piece3;
|
||||
piece3->prev = piece;
|
||||
|
||||
change->chains[1][0] = piece2;
|
||||
change->chains[1][1] = piece;
|
||||
|
||||
change2 = add_change();
|
||||
change2->before = piece;
|
||||
change2->after = c.iterator.piece->next;
|
||||
change2->chains[0][0] = nullptr;
|
||||
change2->chains[0][1] = nullptr;
|
||||
change2->chains[1][0] = piece3;
|
||||
change2->chains[1][1] = nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
change->chains[1][0] = piece;
|
||||
change->chains[1][1] = nullptr;
|
||||
if (c.iterator.piece->length != 0u)
|
||||
{
|
||||
change->after = c.iterator.piece;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
change->before = start_piece;
|
||||
change->after = end_piece;
|
||||
change->chains[0][0] = nullptr;
|
||||
change->chains[0][1] = nullptr;
|
||||
change->chains[1][0] = piece;
|
||||
change->chains[1][1] = nullptr;
|
||||
}
|
||||
|
||||
apply_change(change, 1u);
|
||||
if (change2 != nullptr)
|
||||
{
|
||||
apply_change(change2, 1u);
|
||||
}
|
||||
|
||||
c.warp_to_inserted_piece(piece);
|
||||
}
|
||||
|
||||
void PieceTable::undo()
|
||||
{
|
||||
if (m_changes_index > 0u)
|
||||
{
|
||||
m_changes_index--;
|
||||
apply_change(&m_changes[m_changes_index], 0u);
|
||||
}
|
||||
}
|
||||
|
||||
void PieceTable::apply_change(Change * change, uint8_t forward)
|
||||
{
|
||||
Piece * p = change->before;
|
||||
for (uint8_t i = 0; i < sizeof(change->chains[0]) / sizeof(change->chains[0][0]); i++)
|
||||
{
|
||||
if (change->chains[forward][i] == nullptr)
|
||||
{
|
||||
break;
|
||||
}
|
||||
p->next = change->chains[forward][i];
|
||||
change->chains[forward][i]->prev = p;
|
||||
p = change->chains[forward][i];
|
||||
}
|
||||
p->next = change->after;
|
||||
change->after->prev = p;
|
||||
}
|
@ -1,210 +0,0 @@
|
||||
#ifndef PIECETABLE_H
|
||||
#define PIECETABLE_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include "PagedBuffer.h"
|
||||
#include <utility>
|
||||
#include <memory>
|
||||
#include <list>
|
||||
#include "Encoding.h"
|
||||
|
||||
class PieceTable
|
||||
{
|
||||
public:
|
||||
enum
|
||||
{
|
||||
INTERNAL_EOL = '\n',
|
||||
};
|
||||
|
||||
struct Piece
|
||||
{
|
||||
/** The previous piece in the chain. */
|
||||
Piece * prev;
|
||||
|
||||
/** The next piece in the chain. */
|
||||
Piece * next;
|
||||
|
||||
/** Text that the piece describes. */
|
||||
uint8_t * start;
|
||||
|
||||
/** Length of the text (in bytes). */
|
||||
uint32_t length;
|
||||
|
||||
/** Get whether this piece is the end of a line. */
|
||||
bool eol() { return (length > 0u) && (start[length - 1u] == INTERNAL_EOL); }
|
||||
};
|
||||
|
||||
struct Iterator
|
||||
{
|
||||
/** The piece table the cursor belongs to. */
|
||||
PieceTable * piece_table;
|
||||
|
||||
/** The piece. */
|
||||
Piece * piece;
|
||||
|
||||
/** Byte offset within the piece. */
|
||||
uint32_t offset;
|
||||
|
||||
Iterator(PieceTable * pt);
|
||||
|
||||
/** Get the character pointed to by the cursor. */
|
||||
uint32_t operator*() const
|
||||
{
|
||||
if (has_char())
|
||||
{
|
||||
return Encoding::decode(piece_table->encoding, &piece->start[offset]);
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0xFFFFFFFFu;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t num_bytes_in_code_point() const;
|
||||
|
||||
void go_next_position();
|
||||
void go_prev_position();
|
||||
void go_end_of_piece();
|
||||
void go_next_piece();
|
||||
void go_prev_piece();
|
||||
|
||||
bool valid() const
|
||||
{
|
||||
return (piece != piece_table->start_piece) &&
|
||||
(piece != piece_table->end_piece);
|
||||
}
|
||||
|
||||
bool has_char() const
|
||||
{
|
||||
return valid() && (offset < piece->length);
|
||||
}
|
||||
|
||||
bool operator==(const Iterator & other) const
|
||||
{
|
||||
return (other.piece == piece) && (other.offset == offset);
|
||||
}
|
||||
|
||||
bool operator!=(const Iterator & other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
bool piece_eol()
|
||||
{
|
||||
return piece->eol();
|
||||
}
|
||||
};
|
||||
|
||||
struct Cursor
|
||||
{
|
||||
Iterator iterator;
|
||||
|
||||
uint32_t line;
|
||||
|
||||
uint32_t column;
|
||||
|
||||
/** Get the character pointed to by the cursor. */
|
||||
uint32_t operator*() const
|
||||
{
|
||||
return *iterator;
|
||||
}
|
||||
|
||||
Cursor(PieceTable * pt);
|
||||
|
||||
void go_start_of_line();
|
||||
void go_end_of_line(bool allow_on_eol);
|
||||
void go_up(int n, uint32_t desired_column);
|
||||
void go_down(int n, uint32_t desired_column);
|
||||
void go_left(int n);
|
||||
void go_right(int n, bool allow_on_eol);
|
||||
|
||||
bool check_go_start_of_line();
|
||||
bool check_go_end_of_line(bool allow_on_eol);
|
||||
bool check_go_up(int n, uint32_t desired_column);
|
||||
bool check_go_down(int n, uint32_t desired_column);
|
||||
bool check_go_left(int n);
|
||||
bool check_go_right(int n, bool allow_on_eol);
|
||||
|
||||
bool operator==(const Cursor & c) const
|
||||
{
|
||||
return (c.line == line) && (c.column == column);
|
||||
}
|
||||
bool operator!=(const Cursor & c) const
|
||||
{
|
||||
return !(*this == c);
|
||||
}
|
||||
void warp_to_inserted_piece(Piece * piece);
|
||||
void warp_to_beginning_of_piece(Piece * piece);
|
||||
void calculate_column();
|
||||
|
||||
protected:
|
||||
bool is_start_of_line();
|
||||
bool is_end_of_line();
|
||||
Piece * prev_line() const;
|
||||
Piece * next_line() const;
|
||||
void init_column();
|
||||
void forward_to_column(uint32_t c);
|
||||
};
|
||||
|
||||
struct Change
|
||||
{
|
||||
Piece * before;
|
||||
Piece * after;
|
||||
Piece * chains[2][2];
|
||||
};
|
||||
|
||||
Piece * start_piece;
|
||||
Piece * end_piece;
|
||||
uint8_t tabstop;
|
||||
Encoding::Type encoding;
|
||||
|
||||
PieceTable();
|
||||
|
||||
uint32_t get_num_lines() { return m_num_lines; }
|
||||
|
||||
void append_initial_line_piece(uint8_t * start, uint32_t length, bool eol);
|
||||
|
||||
std::shared_ptr<Cursor> add_cursor();
|
||||
|
||||
void begin_insert(Cursor & cursor, bool before);
|
||||
void insert_code_point(uint32_t code_point);
|
||||
void end_insert();
|
||||
void insertion_test(Cursor & c);
|
||||
|
||||
void undo();
|
||||
|
||||
bool in_insert_mode() { return m_inserting_piece != nullptr; }
|
||||
|
||||
protected:
|
||||
Piece * add_piece()
|
||||
{
|
||||
return &m_pieces[m_piece_index++];
|
||||
}
|
||||
|
||||
Piece & operator[](uint32_t index)
|
||||
{
|
||||
return m_pieces[index];
|
||||
}
|
||||
|
||||
Change * add_change()
|
||||
{
|
||||
return &m_changes[m_changes_index++];
|
||||
}
|
||||
|
||||
void apply_change(Change * change, uint8_t forward);
|
||||
|
||||
uint32_t m_num_lines;
|
||||
|
||||
PagedBuffer<uint8_t> m_append_buffer;
|
||||
uint32_t m_append_buffer_index;
|
||||
PagedBuffer<Piece> m_pieces;
|
||||
/** Next available piece index. */
|
||||
uint32_t m_piece_index;
|
||||
PagedBuffer<Change> m_changes;
|
||||
uint32_t m_changes_index;
|
||||
Piece * m_inserting_piece;
|
||||
|
||||
std::list<std::shared_ptr<Cursor>> m_cursors;
|
||||
};
|
||||
|
||||
#endif
|
@ -144,10 +144,9 @@ bool Window::create(std::shared_ptr<Buffer> buffer)
|
||||
glVertexAttribPointer(0, 2, GL_INT, GL_FALSE, 0, 0);
|
||||
|
||||
m_buffer = buffer;
|
||||
m_cursor = m_buffer->piece_table->add_cursor();
|
||||
m_cursor_row = 0;
|
||||
m_scroll_offset = 5;
|
||||
m_target_column = m_cursor->column;
|
||||
m_target_column = 0u; /* TODO: fix */
|
||||
|
||||
glClearColor (0.0, 0.0, 0.0, 0.0);
|
||||
|
||||
@ -250,6 +249,7 @@ void Window::handle_keysym(uint32_t keysym)
|
||||
|
||||
void Window::handle_keyval(uint32_t keyval)
|
||||
{
|
||||
#if 0
|
||||
if (m_buffer->piece_table->in_insert_mode())
|
||||
{
|
||||
if (keyval == '\033')
|
||||
@ -263,6 +263,7 @@ void Window::handle_keyval(uint32_t keyval)
|
||||
redraw();
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
switch (keyval)
|
||||
{
|
||||
@ -287,27 +288,13 @@ void Window::handle_keyval(uint32_t keyval)
|
||||
case 'l':
|
||||
cursor_move(CURSOR_RIGHT);
|
||||
break;
|
||||
case 'p':
|
||||
insertion_test();
|
||||
break;
|
||||
case 'u':
|
||||
m_buffer->piece_table->undo();
|
||||
redraw();
|
||||
break;
|
||||
case 'i':
|
||||
m_buffer->piece_table->begin_insert(*m_cursor, true);
|
||||
redraw();
|
||||
break;
|
||||
case 'a':
|
||||
m_buffer->piece_table->begin_insert(*m_cursor, false);
|
||||
redraw();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Window::cursor_move(int which)
|
||||
{
|
||||
#if 0
|
||||
bool moved = false;
|
||||
int current_row_offset = m_cursor->column / m_columns;
|
||||
int row_offset = 0;
|
||||
@ -374,6 +361,7 @@ void Window::cursor_move(int which)
|
||||
update_cursor_row(m_cursor_row + row_offset);
|
||||
redraw();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void Window::update_cursor_row(int cursor_row)
|
||||
@ -381,103 +369,8 @@ void Window::update_cursor_row(int cursor_row)
|
||||
m_cursor_row = std::max(m_scroll_offset, std::min(m_rows - m_scroll_offset - 1, cursor_row));
|
||||
}
|
||||
|
||||
std::pair<int, PieceTable::Cursor> Window::calculate_start_position()
|
||||
{
|
||||
m_cursor_row = std::min(m_cursor_row, m_rows - 1);
|
||||
int row = m_cursor_row - m_cursor->column / m_columns;
|
||||
PieceTable::Cursor cursor = *m_cursor;
|
||||
cursor.go_start_of_line();
|
||||
while (row > 0)
|
||||
{
|
||||
if (cursor.check_go_up(1, 0u))
|
||||
{
|
||||
PieceTable::Cursor c = cursor;
|
||||
c.go_end_of_line(false);
|
||||
row -= c.column / m_columns + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_cursor_row -= row;
|
||||
row = 0;
|
||||
}
|
||||
}
|
||||
return std::pair<int, PieceTable::Cursor>(row, cursor);
|
||||
}
|
||||
|
||||
void Window::draw_buffer()
|
||||
{
|
||||
auto start_position = calculate_start_position();
|
||||
int row = start_position.first;
|
||||
PieceTable::Cursor cursor = start_position.second;
|
||||
int last_vertical_crosshair_row = -1;
|
||||
int crosshair_row_offset = m_cursor->column / m_columns;
|
||||
int crosshair_screen_column = m_cursor->column % m_columns;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
if ((last_vertical_crosshair_row < row) &&
|
||||
(cursor.column >= m_cursor->column))
|
||||
{
|
||||
int crosshair_screen_row = row + crosshair_row_offset;
|
||||
if ((crosshair_screen_row >= 0) &&
|
||||
(crosshair_screen_row < m_rows))
|
||||
{
|
||||
draw_crosshair(crosshair_screen_column, crosshair_screen_row);
|
||||
}
|
||||
last_vertical_crosshair_row = row;
|
||||
}
|
||||
int row_offset = cursor.column / m_columns;
|
||||
int screen_row = row + row_offset;
|
||||
if (screen_row >= m_rows)
|
||||
break;
|
||||
int screen_column = cursor.column % m_columns;
|
||||
if (screen_row >= 0)
|
||||
{
|
||||
if (cursor.line == m_cursor->line)
|
||||
draw_crosshair(screen_column, screen_row);
|
||||
if (cursor == *m_cursor)
|
||||
draw_cursor(screen_column, screen_row, m_buffer->piece_table->in_insert_mode());
|
||||
uint32_t character = *cursor;
|
||||
if ((character != 0xFFFFFFFFu) &&
|
||||
(character != ' ') &&
|
||||
(character != '\t') &&
|
||||
(character != '\n'))
|
||||
draw_buffer_character(screen_column, screen_row, character);
|
||||
}
|
||||
bool eol = !cursor.check_go_right(1, true);
|
||||
bool draw_insert_mode_cursor = false;
|
||||
if (!eol && *cursor == PieceTable::INTERNAL_EOL)
|
||||
{
|
||||
if ((cursor == *m_cursor) && m_buffer->piece_table->in_insert_mode())
|
||||
{
|
||||
row_offset = cursor.column / m_columns;
|
||||
screen_row = row + row_offset;
|
||||
screen_column = cursor.column % m_columns;
|
||||
draw_insert_mode_cursor = true;
|
||||
}
|
||||
eol = true;
|
||||
}
|
||||
if (eol)
|
||||
{
|
||||
bool last_row = !cursor.check_go_down(1, 0);
|
||||
if (last_vertical_crosshair_row < row)
|
||||
{
|
||||
int crosshair_screen_row = row + crosshair_row_offset;
|
||||
if (crosshair_screen_row == screen_row)
|
||||
{
|
||||
draw_crosshair(crosshair_screen_column, crosshair_screen_row);
|
||||
}
|
||||
last_vertical_crosshair_row = row;
|
||||
}
|
||||
if (draw_insert_mode_cursor)
|
||||
{
|
||||
draw_cursor(screen_column, screen_row, true);
|
||||
}
|
||||
if (last_row)
|
||||
break;
|
||||
row = screen_row + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Window::draw_buffer_character(int screen_column, int screen_row, uint32_t character)
|
||||
@ -562,6 +455,7 @@ void Window::draw_status_bar()
|
||||
{
|
||||
draw_rect(0, m_font.get_line_height(), m_width, 1, 0.5, 0.5, 0.5, 1.0);
|
||||
draw_rect(0, 0, m_width, m_font.get_line_height(), 0.0, 0.0, 0.0, 1.0);
|
||||
#if 0
|
||||
char cursor_position[20];
|
||||
sprintf(cursor_position, "%d, %d", m_cursor->line + 1, m_cursor->column + 1u);
|
||||
int cursor_position_length = strlen(cursor_position);
|
||||
@ -571,12 +465,7 @@ void Window::draw_status_bar()
|
||||
draw_character(x, 0, cursor_position[i]);
|
||||
x += m_font.get_advance();
|
||||
}
|
||||
}
|
||||
|
||||
void Window::insertion_test()
|
||||
{
|
||||
m_buffer->piece_table->insertion_test(*m_cursor);
|
||||
redraw();
|
||||
#endif
|
||||
}
|
||||
|
||||
uint32_t Window::get_keyval(SDL_Keycode keysym)
|
||||
|
@ -44,14 +44,12 @@ protected:
|
||||
void handle_keysym(uint32_t keysym);
|
||||
void handle_keyval(uint32_t keyval);
|
||||
void cursor_move(int which);
|
||||
std::pair<int, PieceTable::Cursor> calculate_start_position();
|
||||
void draw_buffer();
|
||||
void draw_buffer_character(int screen_column, int screen_row, uint32_t character);
|
||||
void draw_character(int x, int y, uint32_t character);
|
||||
void update_cursor_row(int cursor_row);
|
||||
void draw_rect(int x, int y, int width, int height, float r, float g, float b, float a);
|
||||
void draw_status_bar();
|
||||
void insertion_test();
|
||||
uint32_t get_keyval(SDL_Keycode keysym);
|
||||
uint32_t get_shifted(uint32_t keysym);
|
||||
|
||||
@ -80,8 +78,6 @@ protected:
|
||||
std::shared_ptr<glcxx::Array> m_rect_array;
|
||||
std::shared_ptr<glcxx::Buffer> m_rect_buffer;
|
||||
|
||||
std::shared_ptr<PieceTable::Cursor> m_cursor;
|
||||
|
||||
Uint16 m_keymod;
|
||||
};
|
||||
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include "TestSupport.h"
|
||||
#include "Path.h"
|
||||
|
||||
#if 0
|
||||
TEST(BufferTest, writes_an_identical_file_to_what_is_loaded_if_no_changes_were_performed)
|
||||
{
|
||||
const char * files_to_test[] = {
|
||||
@ -41,3 +42,4 @@ TEST(BufferTest, allows_navigating_using_cursors)
|
||||
EXPECT_FALSE(cursor->check_go_down(1, cursor->column));
|
||||
ASSERT_EQ((uint32_t)'T', **cursor);
|
||||
}
|
||||
#endif
|
||||
|
Loading…
x
Reference in New Issue
Block a user