jes/src/core/PieceTable.cc

331 lines
7.3 KiB
C++

#include "PieceTable.h"
PieceTable::PieceTable(const uint8_t * file_buffer, unsigned long file_buffer_size)
{
m_file_buffer = file_buffer;
m_file_buffer_size = file_buffer_size;
start_piece = &m_pieces[PIECE_INDEX_START];
end_piece = &m_pieces[PIECE_INDEX_END];
start_piece->next = end_piece;
end_piece->prev = start_piece;
m_piece_index = 2u;
tabstop = 4u;
}
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;
piece->flags = 0u;
if (eol)
piece->flags |= Piece::EOL_FLAG;
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;
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()
{
uint8_t code_point_size = num_bytes_in_code_point();
if ((offset + code_point_size) >= piece->length)
{
go_next_piece();
return;
}
offset += code_point_size;
}
void PieceTable::Iterator::go_prev_position()
{
if (offset > 0)
{
offset = Encoding::beginning_of_code_point(piece_table->encoding, &piece->start[offset - 1u]) - piece->start;
}
else if (piece != piece_table->start_piece)
{
piece = piece->prev;
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)
{
piece = piece->next;
offset = 0u;
}
}
void PieceTable::Iterator::go_prev_piece()
{
if (piece != piece_table->start_piece)
{
piece = piece->prev;
offset = 0u;
}
}
PieceTable::Cursor::Cursor(PieceTable * pt) : iterator(pt)
{
line = 0u;
init_column();
}
bool PieceTable::Cursor::go_start_of_line()
{
if (!iterator.valid())
return false;
bool rv = false;
while (iterator.valid() &&
(!iterator.piece->prev->eol()) &&
(iterator.piece->prev != iterator.piece_table->start_piece))
{
iterator.go_prev_piece();
rv = true;
}
if (iterator.offset != 0u)
rv = true;
iterator.offset = 0u;
init_column();
return rv;
}
bool PieceTable::Cursor::go_end_of_line()
{
if (!iterator.valid())
return false;
bool rv = false;
while (iterator.valid() &&
(!iterator.piece->eol()) &&
(iterator.piece->next != iterator.piece_table->end_piece))
{
iterator.go_next_piece();
rv = true;
}
uint32_t current_column = column;
iterator.go_end_of_piece();
calculate_column();
if (column != current_column)
rv = true;
return rv;
}
bool PieceTable::Cursor::go_up(int n, uint32_t desired_column)
{
if (!iterator.valid())
return false;
Piece * p = prev_line();
if (p == nullptr)
{
return false;
}
iterator.piece = p;
iterator.offset = 0u;
line--;
init_column();
forward_to_column(desired_column);
return true;
}
bool PieceTable::Cursor::go_down(int n, uint32_t desired_column)
{
if (!iterator.valid())
return false;
Piece * p = next_line();
if (p == nullptr)
{
return false;
}
iterator.piece = p;
iterator.offset = 0u;
line++;
init_column();
forward_to_column(desired_column);
return true;
}
bool PieceTable::Cursor::go_left(int n)
{
if (!iterator.valid())
return false;
if (is_start_of_line())
{
return false;
}
uint32_t chr = *iterator;
iterator.go_prev_position();
if (chr == '\t')
{
calculate_column();
}
else
{
/* TODO: handle multi-column characters */
column--;
}
return true;
}
bool PieceTable::Cursor::go_right(int n)
{
if (!iterator.valid())
return false;
if (is_end_of_line())
{
return false;
}
iterator.go_next_position();
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++;
}
return true;
}
bool PieceTable::Cursor::is_start_of_line()
{
return iterator.valid() &&
(iterator.offset == 0u) &&
((iterator.piece->prev == iterator.piece_table->start_piece) ||
iterator.piece->prev->eol());
}
bool PieceTable::Cursor::is_end_of_line()
{
return iterator.valid() &&
((iterator.offset + iterator.num_bytes_in_code_point()) >= iterator.piece->length) &&
(iterator.piece->eol() || (iterator.piece->next == iterator.piece_table->end_piece));
}
PieceTable::Piece * PieceTable::Cursor::prev_line() const
{
Piece * p = iterator.piece;
while ((!p->prev->eol()) && (p->prev != iterator.piece_table->start_piece))
{
p = p->prev;
}
if (p->prev == iterator.piece_table->start_piece)
{
return nullptr;
}
p = p->prev;
while ((!p->prev->eol()) && (p->prev != iterator.piece_table->start_piece))
{
p = p->prev;
}
return p;
}
PieceTable::Piece * PieceTable::Cursor::next_line() const
{
Piece * p = iterator.piece;
while ((!p->eol()) && (p->next != iterator.piece_table->end_piece))
{
p = p->next;
}
if (p->next == iterator.piece_table->end_piece)
{
return nullptr;
}
p = p->next;
while ((!p->eol()) && (p->next != iterator.piece_table->end_piece))
{
p = p->next;
}
return p;
}
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);
}
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 (go_right(1))
{
int32_t new_diff = abs((int32_t)(c - column));
if (new_diff > diff)
{
go_left(1);
return;
}
diff = new_diff;
}
}