Refactor WrapHighlighter::next_split_pos to avoid non-linear complexity
Previous Implementation was constantly computing byte/column count from the begining of the line, leading to a non-linear complexity with respect to the length of a line. Fixes #2146
This commit is contained in:
parent
51ec1194f1
commit
1b5f665664
|
@ -643,6 +643,8 @@ struct WrapHighlighter : Highlighter
|
||||||
|
|
||||||
static constexpr StringView ms_id = "wrap";
|
static constexpr StringView ms_id = "wrap";
|
||||||
|
|
||||||
|
struct SplitPos{ ByteCount byte; ColumnCount column; };
|
||||||
|
|
||||||
void do_highlight(HighlightContext context, DisplayBuffer& display_buffer, BufferRange) override
|
void do_highlight(HighlightContext context, DisplayBuffer& display_buffer, BufferRange) override
|
||||||
{
|
{
|
||||||
if (contains(context.disabled_ids, ms_id))
|
if (contains(context.disabled_ids, ms_id))
|
||||||
|
@ -665,58 +667,59 @@ struct WrapHighlighter : Highlighter
|
||||||
const ByteCount line_length = buffer[buf_line].length();
|
const ByteCount line_length = buffer[buf_line].length();
|
||||||
const ColumnCount indent = m_preserve_indent ? line_indent(buffer, tabstop, buf_line) : 0_col;
|
const ColumnCount indent = m_preserve_indent ? line_indent(buffer, tabstop, buf_line) : 0_col;
|
||||||
|
|
||||||
auto coord = next_split_coord(buffer, wrap_column, tabstop, buf_line);
|
auto pos = next_split_pos(buffer, wrap_column, tabstop, buf_line, {0, 0});
|
||||||
if (buffer.is_valid(coord) and not buffer.is_end(coord))
|
if (pos.byte == line_length)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
for (auto atom_it = it->begin();
|
||||||
|
pos.byte != line_length and atom_it != it->end(); )
|
||||||
{
|
{
|
||||||
for (auto atom_it = it->begin();
|
const BufferCoord coord{buf_line, pos.byte};
|
||||||
coord.column != line_length and atom_it != it->end(); )
|
if (!atom_it->has_buffer_range() or
|
||||||
|
coord < atom_it->begin() or coord >= atom_it->end())
|
||||||
{
|
{
|
||||||
if (!atom_it->has_buffer_range() or
|
++atom_it;
|
||||||
coord < atom_it->begin() or coord >= atom_it->end())
|
continue;
|
||||||
{
|
|
||||||
++atom_it;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto& line = *it;
|
|
||||||
|
|
||||||
if (coord > atom_it->begin())
|
|
||||||
atom_it = ++line.split(atom_it, coord);
|
|
||||||
|
|
||||||
DisplayLine new_line{ AtomList{ atom_it, line.end() } };
|
|
||||||
line.erase(atom_it, line.end());
|
|
||||||
|
|
||||||
ColumnCount prefix_len = 0;
|
|
||||||
if (marker_len != 0 and marker_len < wrap_column)
|
|
||||||
{
|
|
||||||
new_line.insert(new_line.begin(), {m_marker, face_marker});
|
|
||||||
prefix_len = marker_len;
|
|
||||||
}
|
|
||||||
if (indent > marker_len and indent < wrap_column)
|
|
||||||
{
|
|
||||||
auto it = new_line.insert(new_line.begin() + (marker_len > 0), {buffer, coord, coord});
|
|
||||||
it->replace(String{' ', indent - marker_len});
|
|
||||||
prefix_len = indent;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (it+1 - display_buffer.lines().begin() == win_height)
|
|
||||||
{
|
|
||||||
if (cursor >= new_line.range().begin) // strip first lines if cursor is not visible
|
|
||||||
{
|
|
||||||
display_buffer.lines().erase(display_buffer.lines().begin(), display_buffer.lines().begin()+1);
|
|
||||||
--it;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
display_buffer.lines().erase(it+1, display_buffer.lines().end());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
it = display_buffer.lines().insert(it+1, new_line);
|
|
||||||
|
|
||||||
coord = next_split_coord(buffer, wrap_column - prefix_len, tabstop, coord);
|
|
||||||
atom_it = it->begin();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto& line = *it;
|
||||||
|
|
||||||
|
if (coord > atom_it->begin())
|
||||||
|
atom_it = ++line.split(atom_it, coord);
|
||||||
|
|
||||||
|
DisplayLine new_line{ AtomList{ atom_it, line.end() } };
|
||||||
|
line.erase(atom_it, line.end());
|
||||||
|
|
||||||
|
ColumnCount prefix_len = 0;
|
||||||
|
if (marker_len != 0 and marker_len < wrap_column)
|
||||||
|
{
|
||||||
|
new_line.insert(new_line.begin(), {m_marker, face_marker});
|
||||||
|
prefix_len = marker_len;
|
||||||
|
}
|
||||||
|
if (indent > marker_len and indent < wrap_column)
|
||||||
|
{
|
||||||
|
auto it = new_line.insert(new_line.begin() + (marker_len > 0), {buffer, coord, coord});
|
||||||
|
it->replace(String{' ', indent - marker_len});
|
||||||
|
prefix_len = indent;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (it+1 - display_buffer.lines().begin() == win_height)
|
||||||
|
{
|
||||||
|
if (cursor >= new_line.range().begin) // strip first lines if cursor is not visible
|
||||||
|
{
|
||||||
|
display_buffer.lines().erase(display_buffer.lines().begin(), display_buffer.lines().begin()+1);
|
||||||
|
--it;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
display_buffer.lines().erase(it+1, display_buffer.lines().end());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
it = display_buffer.lines().insert(it+1, new_line);
|
||||||
|
|
||||||
|
pos = next_split_pos(buffer, wrap_column - prefix_len, tabstop, buf_line, pos);
|
||||||
|
atom_it = it->begin();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -736,13 +739,13 @@ struct WrapHighlighter : Highlighter
|
||||||
|
|
||||||
auto line_wrap_count = [&](LineCount line, ColumnCount indent) {
|
auto line_wrap_count = [&](LineCount line, ColumnCount indent) {
|
||||||
LineCount count = 0;
|
LineCount count = 0;
|
||||||
BufferCoord coord{line};
|
|
||||||
const ByteCount line_length = buffer[line].length();
|
const ByteCount line_length = buffer[line].length();
|
||||||
|
SplitPos pos{0, 0};
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
coord = next_split_coord(buffer, wrap_column - (coord.column == 0 ? 0_col : indent),
|
pos = next_split_pos(buffer, wrap_column - (pos.byte == 0 ? 0_col : indent),
|
||||||
tabstop, coord);
|
tabstop, line, pos);
|
||||||
if (coord.column == line_length)
|
if (pos.byte == line_length)
|
||||||
break;
|
break;
|
||||||
++count;
|
++count;
|
||||||
}
|
}
|
||||||
|
@ -776,22 +779,21 @@ struct WrapHighlighter : Highlighter
|
||||||
|
|
||||||
if (buf_line == cursor.line)
|
if (buf_line == cursor.line)
|
||||||
{
|
{
|
||||||
BufferCoord coord{buf_line};
|
SplitPos pos{0, 0};
|
||||||
for (LineCount count = 0; true; ++count)
|
for (LineCount count = 0; true; ++count)
|
||||||
{
|
{
|
||||||
auto split_coord = next_split_coord(buffer, wrap_column - (coord.column != 0 ? prefix_len : 0_col),
|
auto next_pos = next_split_pos(buffer, wrap_column - (pos.byte != 0 ? prefix_len : 0_col),
|
||||||
tabstop, coord);
|
tabstop, buf_line, pos);
|
||||||
if (split_coord.column > cursor.column)
|
if (next_pos.byte > cursor.column)
|
||||||
{
|
{
|
||||||
setup.cursor_pos = DisplayCoord{
|
setup.cursor_pos = DisplayCoord{
|
||||||
win_line + count,
|
win_line + count,
|
||||||
get_column(buffer, tabstop, cursor) -
|
get_column(buffer, tabstop, cursor) -
|
||||||
get_column(buffer, tabstop, coord) +
|
pos.column + (pos.byte != 0 ? indent : 0_col)
|
||||||
(coord.column != 0 ? indent : 0_col)
|
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
coord = split_coord;
|
pos = next_pos;
|
||||||
}
|
}
|
||||||
kak_assert(setup.cursor_pos.column >= 0 and setup.cursor_pos.column < setup.window_range.column);
|
kak_assert(setup.cursor_pos.column >= 0 and setup.cursor_pos.column < setup.window_range.column);
|
||||||
}
|
}
|
||||||
|
@ -817,28 +819,48 @@ struct WrapHighlighter : Highlighter
|
||||||
unique_ids.push_back(ms_id);
|
unique_ids.push_back(ms_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
BufferCoord next_split_coord(const Buffer& buffer, ColumnCount wrap_column, int tabstop, BufferCoord coord) const
|
SplitPos next_split_pos(const Buffer& buffer, ColumnCount wrap_column, int tabstop, LineCount line, SplitPos current) const
|
||||||
{
|
{
|
||||||
auto column = get_column(buffer, tabstop, coord);
|
const ColumnCount target_column = current.column + wrap_column;
|
||||||
auto col = get_byte_to_column(
|
StringView content = buffer[line];
|
||||||
buffer, tabstop, {coord.line, column + wrap_column});
|
|
||||||
StringView line = buffer[coord.line];
|
|
||||||
if (col == coord.column) // Can happen if we try to wrap on a tab char
|
|
||||||
col = line.byte_count_to(line.char_count_to(coord.column)+1);
|
|
||||||
|
|
||||||
BufferCoord split_coord{coord.line, col};
|
SplitPos pos = current;
|
||||||
|
while (pos.byte < content.length() and pos.column < target_column)
|
||||||
if (m_word_wrap)
|
|
||||||
{
|
{
|
||||||
utf8::iterator<const char*> it{&line[col], line};
|
if (content[pos.byte] == '\t')
|
||||||
while (it != line.end() and it != line.begin() and is_word<WORD>(*it))
|
{
|
||||||
|
const ColumnCount next_column = (pos.column / tabstop + 1) * tabstop;
|
||||||
|
if (next_column > target_column and pos.byte != current.byte) // the target column was in the tab
|
||||||
|
break;
|
||||||
|
pos.column = next_column;
|
||||||
|
++pos.byte;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const char* it = &content[pos.byte];
|
||||||
|
const ColumnCount width = codepoint_width(utf8::read_codepoint(it, content.end()));
|
||||||
|
if (pos.column + width > target_column and pos.byte != current.byte) // the target column was in the char
|
||||||
|
break;
|
||||||
|
pos.column += width;
|
||||||
|
pos.byte = (int)(it - content.begin());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_word_wrap and pos.byte < content.length()) // find a word boundary before current position
|
||||||
|
{
|
||||||
|
utf8::iterator<const char*> it{&content[pos.byte], content};
|
||||||
|
while (it != content.begin() and is_word<WORD>(*it))
|
||||||
--it;
|
--it;
|
||||||
|
|
||||||
if (it != line.begin() and it != &line[col] and
|
if (it != content.begin() and it != &content[pos.byte] and
|
||||||
(it+1) > &line[coord.column])
|
(it+1) > &content[current.byte])
|
||||||
split_coord.column = (it+1).base() - line.begin();
|
{
|
||||||
|
const ByteCount word_split = (it+1).base() - content.begin();
|
||||||
|
pos.column -= content.substr(word_split, pos.byte - word_split).column_length();
|
||||||
|
pos.byte = word_split;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return split_coord;
|
return pos;
|
||||||
};
|
};
|
||||||
|
|
||||||
ColumnCount line_indent(const Buffer& buffer, int tabstop, LineCount line) const
|
ColumnCount line_indent(const Buffer& buffer, int tabstop, LineCount line) const
|
||||||
|
|
Loading…
Reference in New Issue
Block a user