DisplayBuffer: refactoring, correct support of replacements

DisplayAtoms now know where they are on the screen and have
line_and_column_at and iterator_at methods.
This commit is contained in:
Maxime Coste 2011-10-15 04:45:49 +00:00
parent 187a99aba2
commit dc74934e59
7 changed files with 240 additions and 74 deletions

View File

@ -26,9 +26,7 @@ struct BufferCoord : LineAndColumn<BufferCoord>
template<typename T> template<typename T>
explicit BufferCoord(const LineAndColumn<T>& other) explicit BufferCoord(const LineAndColumn<T>& other)
: LineAndColumn(other.line, other.column) : LineAndColumn(other.line, other.column) {}
{
}
}; };
class BufferIterator class BufferIterator

View File

@ -5,6 +5,76 @@
namespace Kakoune namespace Kakoune
{ {
BufferString DisplayAtom::content() const
{
if (m_replacement_text.empty())
return m_begin.buffer().string(m_begin, m_end);
else
return m_replacement_text;
}
template<typename Iterator>
static DisplayCoord advance_coord(const DisplayCoord& pos,
Iterator begin, Iterator end)
{
DisplayCoord res = pos;
while (begin != end)
{
if (*begin == '\n')
{
++res.line;
res.column = 0;
}
else
++res.column;
++begin;
}
return res;
}
DisplayCoord DisplayAtom::end_coord() const
{
if (m_replacement_text.empty())
return advance_coord(m_coord, m_begin, m_end);
else
return advance_coord(m_coord, m_replacement_text.begin(),
m_replacement_text.end());
}
BufferIterator DisplayAtom::iterator_at(const DisplayCoord& coord) const
{
if (not m_replacement_text.empty() or coord <= m_coord)
return m_begin;
DisplayCoord pos = m_coord;
for (BufferIterator it = m_begin; it != m_end; ++it)
{
if (*it == '\n')
{
++pos.line;
pos.column = 0;
}
else
++pos.column;
if (coord == pos)
return it+1;
else if (coord < pos)
return it;
}
return m_end;
}
DisplayCoord DisplayAtom::line_and_column_at(const BufferIterator& iterator) const
{
assert(iterator >= m_begin and iterator < m_end);
if (not m_replacement_text.empty())
return m_coord;
return advance_coord(m_coord, m_begin, iterator);
}
DisplayBuffer::DisplayBuffer() DisplayBuffer::DisplayBuffer()
{ {
} }
@ -12,22 +82,47 @@ DisplayBuffer::DisplayBuffer()
DisplayBuffer::iterator DisplayBuffer::split(iterator atom, const BufferIterator& pos) DisplayBuffer::iterator DisplayBuffer::split(iterator atom, const BufferIterator& pos)
{ {
assert(atom < end()); assert(atom < end());
assert(pos > atom->begin); assert(pos > atom->begin());
assert(pos < atom->end); assert(pos < atom->end());
DisplayAtom new_atom(atom->begin, pos, DisplayAtom new_atom(atom->coord(), atom->begin(), pos,
atom->fg_color, atom->bg_color, atom->attribute); atom->fg_color(), atom->bg_color(), atom->attribute());
atom->begin = pos; atom->m_begin = pos;
return insert(atom, std::move(new_atom)); atom->m_coord = new_atom.end_coord();
iterator res = insert(atom, std::move(new_atom));
check_invariant();
return res;
} }
void DisplayBuffer::check_invariant() const void DisplayBuffer::check_invariant() const
{ {
for (size_t i = 0; i < m_atoms.size(); ++i) for (size_t i = 0; i < m_atoms.size(); ++i)
{ {
assert(m_atoms[i].end > m_atoms[i].begin); assert(m_atoms[i].end() > m_atoms[i].begin());
if (i > 0) if (i > 0)
assert(m_atoms[i-1].end == m_atoms[i].begin); {
assert(m_atoms[i-1].end() == m_atoms[i].begin());
assert(m_atoms[i-1].end_coord() == m_atoms[i].coord());
}
}
}
void DisplayBuffer::replace_atom_content(iterator atom,
const BufferString& replacement)
{
assert(atom < end());
atom->m_replacement_text = replacement;
// update coordinates of subsequents atoms
DisplayCoord new_coord = atom->end_coord();
while (true)
{
new_coord = atom->end_coord();
++atom;
if (atom == end() or new_coord == atom->m_coord)
break;
atom->m_coord = new_coord;
} }
} }

View File

@ -15,6 +15,10 @@ struct DisplayCoord : LineAndColumn<DisplayCoord>
{ {
DisplayCoord(int line = 0, int column = 0) DisplayCoord(int line = 0, int column = 0)
: LineAndColumn(line, column) {} : LineAndColumn(line, column) {}
template<typename T>
explicit DisplayCoord(const LineAndColumn<T>& other)
: LineAndColumn(other.line, other.column) {}
}; };
typedef int Attribute; typedef int Attribute;
@ -43,23 +47,45 @@ enum class Color
struct DisplayAtom struct DisplayAtom
{ {
BufferIterator begin; DisplayAtom(const DisplayCoord& coord,
BufferIterator end; const BufferIterator& begin, const BufferIterator& end,
Color fg_color;
Color bg_color;
Attribute attribute;
BufferString replacement_text;
DisplayAtom(BufferIterator begin, BufferIterator end,
Color fg_color = Color::Default, Color fg_color = Color::Default,
Color bg_color = Color::Default, Color bg_color = Color::Default,
Attribute attribute = Attributes::Normal) Attribute attribute = Attributes::Normal)
: begin(begin), : m_coord(coord),
end(end), m_begin(begin), m_end(end),
fg_color(fg_color), m_fg_color(fg_color),
bg_color(bg_color), m_bg_color(bg_color),
attribute(attribute) m_attribute(attribute)
{} {}
const DisplayCoord& coord() const { return m_coord; }
const BufferIterator& begin() const { return m_begin; }
const BufferIterator& end() const { return m_end; }
const Color& fg_color() const { return m_fg_color; }
const Color& bg_color() const { return m_bg_color; }
const Attribute& attribute() const { return m_attribute; }
Color& fg_color() { return m_fg_color; }
Color& bg_color() { return m_bg_color; }
Attribute& attribute() { return m_attribute; }
BufferString content() const;
DisplayCoord end_coord() const;
BufferIterator iterator_at(const DisplayCoord& coord) const;
DisplayCoord line_and_column_at(const BufferIterator& iterator) const;
private:
friend class DisplayBuffer;
DisplayCoord m_coord;
BufferIterator m_begin;
BufferIterator m_end;
Color m_fg_color;
Color m_bg_color;
Attribute m_attribute;
BufferString m_replacement_text;
}; };
class DisplayBuffer class DisplayBuffer
@ -76,12 +102,17 @@ public:
iterator insert(iterator where, const DisplayAtom& atom) { return m_atoms.insert(where, atom); } iterator insert(iterator where, const DisplayAtom& atom) { return m_atoms.insert(where, atom); }
iterator split(iterator atom, const BufferIterator& pos); iterator split(iterator atom, const BufferIterator& pos);
void replace_atom_content(iterator atom, const BufferString& replacement);
iterator begin() { return m_atoms.begin(); } iterator begin() { return m_atoms.begin(); }
iterator end() { return m_atoms.end(); } iterator end() { return m_atoms.end(); }
const_iterator begin() const { return m_atoms.begin(); } const_iterator begin() const { return m_atoms.begin(); }
const_iterator end() const { return m_atoms.end(); } const_iterator end() const { return m_atoms.end(); }
const DisplayAtom& front() const { return m_atoms.front(); }
const DisplayAtom& back() const { return m_atoms.back(); }
void check_invariant() const; void check_invariant() const;
private: private:
AtomList m_atoms; AtomList m_atoms;

View File

@ -10,17 +10,18 @@ void colorize_regex(DisplayBuffer& display_buffer,
atom_it != display_buffer.end(); ++atom_it) atom_it != display_buffer.end(); ++atom_it)
{ {
boost::match_results<BufferIterator> matches; boost::match_results<BufferIterator> matches;
if (boost::regex_search(atom_it->begin, atom_it->end, matches, ex, boost::match_nosubs)) if (boost::regex_search(atom_it->begin(), atom_it->end(),
matches, ex, boost::match_nosubs))
{ {
const BufferIterator& begin = matches.begin()->first; const BufferIterator& begin = matches.begin()->first;
if (begin != atom_it->begin) if (begin != atom_it->begin())
atom_it = display_buffer.split(atom_it, begin) + 1; atom_it = display_buffer.split(atom_it, begin) + 1;
const BufferIterator& end = matches.begin()->second; const BufferIterator& end = matches.begin()->second;
if (end != atom_it->end) if (end != atom_it->end())
atom_it = display_buffer.split(atom_it, end); atom_it = display_buffer.split(atom_it, end);
atom_it->fg_color = color; atom_it->fg_color() = color;
} }
} }
} }
@ -58,19 +59,32 @@ void expand_tabulations(DisplayBuffer& display_buffer)
for (auto atom_it = display_buffer.begin(); for (auto atom_it = display_buffer.begin();
atom_it != display_buffer.end(); ++atom_it) atom_it != display_buffer.end(); ++atom_it)
{ {
for (BufferIterator it = atom_it->begin; it != atom_it->end; ++it) for (BufferIterator it = atom_it->begin(); it != atom_it->end(); ++it)
{ {
if (*it == '\t') if (*it == '\t')
{ {
if (it != atom_it->begin) if (it != atom_it->begin())
atom_it = display_buffer.split(atom_it, it) + 1; atom_it = display_buffer.split(atom_it, it) + 1;
if (it+1 != atom_it->end) if (it+1 != atom_it->end())
atom_it = display_buffer.split(atom_it, it+1); atom_it = display_buffer.split(atom_it, it+1);
BufferCoord pos = it.buffer().line_and_column_at(it); BufferCoord pos = it.buffer().line_and_column_at(it);
int count = tabstop - (pos.column % tabstop);
atom_it->replacement_text = std::string(count, ' '); int column = 0;
for (auto line_it = it.buffer().iterator_at({pos.line, 0});
line_it != it; ++line_it)
{
assert(*line_it != '\n');
if (*line_it == '\t')
column += tabstop - (column % tabstop);
else
++column;
}
int count = tabstop - (column % tabstop);
display_buffer.replace_atom_content(atom_it,
std::string(count, ' '));
} }
} }
} }

View File

@ -85,15 +85,15 @@ void draw_window(Window& window)
DisplayCoord position; DisplayCoord position;
for (const DisplayAtom& atom : window.display_buffer()) for (const DisplayAtom& atom : window.display_buffer())
{ {
const std::string content = atom.replacement_text.empty() ? assert(position == atom.coord());
window.buffer().string(atom.begin, atom.end) : atom.replacement_text; const std::string content = atom.content();
set_attribute(A_UNDERLINE, atom.attribute & Underline); set_attribute(A_UNDERLINE, atom.attribute() & Underline);
set_attribute(A_REVERSE, atom.attribute & Reverse); set_attribute(A_REVERSE, atom.attribute() & Reverse);
set_attribute(A_BLINK, atom.attribute & Blink); set_attribute(A_BLINK, atom.attribute() & Blink);
set_attribute(A_BOLD, atom.attribute & Bold); set_attribute(A_BOLD, atom.attribute() & Bold);
set_color(atom.fg_color, atom.bg_color); set_color(atom.fg_color(), atom.bg_color());
size_t pos = 0; size_t pos = 0;
size_t end; size_t end;

View File

@ -60,40 +60,40 @@ public:
DisplayAtom& atom = *atom_it; DisplayAtom& atom = *atom_it;
// [###------] // [###------]
if (atom.begin >= sel.begin() and atom.begin < sel.end() and atom.end > sel.end()) if (atom.begin() >= sel.begin() and atom.begin() < sel.end() and atom.end() > sel.end())
{ {
atom_it = display_buffer.split(atom_it, sel.end()); atom_it = display_buffer.split(atom_it, sel.end());
atom_it->attribute |= Attributes::Underline; atom_it->attribute() |= Attributes::Underline;
++atom_it; ++atom_it;
++sel_it; ++sel_it;
} }
// [---###---] // [---###---]
else if (atom.begin < sel.begin() and atom.end > sel.end()) else if (atom.begin() < sel.begin() and atom.end() > sel.end())
{ {
atom_it = display_buffer.split(atom_it, sel.begin()); atom_it = display_buffer.split(atom_it, sel.begin());
atom_it = display_buffer.split(atom_it + 1, sel.end()); atom_it = display_buffer.split(atom_it + 1, sel.end());
atom_it->attribute |= Attributes::Underline; atom_it->attribute() |= Attributes::Underline;
++atom_it; ++atom_it;
++sel_it; ++sel_it;
} }
// [------###] // [------###]
else if (atom.begin < sel.begin() and atom.end > sel.begin()) else if (atom.begin() < sel.begin() and atom.end() > sel.begin())
{ {
atom_it = display_buffer.split(atom_it, sel.begin()) + 1; atom_it = display_buffer.split(atom_it, sel.begin()) + 1;
atom_it->attribute |= Attributes::Underline; atom_it->attribute() |= Attributes::Underline;
++atom_it; ++atom_it;
} }
// [#########] // [#########]
else if (atom.begin >= sel.begin() and atom.end <= sel.end()) else if (atom.begin() >= sel.begin() and atom.end() <= sel.end())
{ {
atom_it->attribute |= Attributes::Underline; atom_it->attribute() |= Attributes::Underline;
++atom_it; ++atom_it;
} }
// [---------] // [---------]
else if (atom.begin >= sel.end()) else if (atom.begin() >= sel.end())
++sel_it; ++sel_it;
// [---------] // [---------]
else if (atom.end <= sel.begin()) else if (atom.end() <= sel.begin())
++atom_it; ++atom_it;
else else
assert(false); assert(false);
@ -124,7 +124,13 @@ void Window::check_invariant() const
DisplayCoord Window::cursor_position() const DisplayCoord Window::cursor_position() const
{ {
check_invariant(); check_invariant();
return line_and_column_at(m_selections.back().last()); return line_and_column_at(cursor_iterator());
}
BufferIterator Window::cursor_iterator() const
{
check_invariant();
return m_selections.back().last();
} }
void Window::erase() void Window::erase()
@ -206,26 +212,47 @@ bool Window::redo()
return m_buffer.redo(); return m_buffer.redo();
} }
BufferCoord Window::window_to_buffer(const DisplayCoord& window_pos) const
{
return BufferCoord(m_position.line + window_pos.line,
m_position.column + window_pos.column);
}
DisplayCoord Window::buffer_to_window(const BufferCoord& buffer_pos) const
{
return DisplayCoord(buffer_pos.line - m_position.line,
buffer_pos.column - m_position.column);
}
BufferIterator Window::iterator_at(const DisplayCoord& window_pos) const BufferIterator Window::iterator_at(const DisplayCoord& window_pos) const
{ {
return m_buffer.iterator_at(window_to_buffer(window_pos)); if (m_display_buffer.begin() == m_display_buffer.end())
return m_buffer.begin();
if (DisplayCoord(0,0) <= window_pos)
{
for (auto atom_it = m_display_buffer.begin();
atom_it != m_display_buffer.end(); ++atom_it)
{
if (window_pos < atom_it->coord())
{
const DisplayAtom& atom = *(atom_it - 1);
return atom.iterator_at(window_pos);
}
}
}
return m_buffer.iterator_at(m_position + BufferCoord(window_pos));
} }
DisplayCoord Window::line_and_column_at(const BufferIterator& iterator) const DisplayCoord Window::line_and_column_at(const BufferIterator& iterator) const
{ {
return buffer_to_window(m_buffer.line_and_column_at(iterator)); if (m_display_buffer.begin() == m_display_buffer.end())
return DisplayCoord(0, 0);
if (iterator >= m_display_buffer.front().begin() and
iterator < m_display_buffer.back().end())
{
for (auto& atom : m_display_buffer)
{
if (atom.end() > iterator)
{
assert(atom.begin() <= iterator);
return atom.line_and_column_at(iterator);
}
}
}
BufferCoord coord = m_buffer.line_and_column_at(iterator);
return DisplayCoord(coord.line - m_position.line,
coord.column - m_position.column);
} }
void Window::clear_selections() void Window::clear_selections()
@ -268,13 +295,16 @@ BufferString Window::selection_content() const
void Window::move_cursor(const DisplayCoord& offset, bool append) void Window::move_cursor(const DisplayCoord& offset, bool append)
{ {
if (not append) if (not append)
move_cursor_to(iterator_at(cursor_position() + offset)); {
BufferCoord pos = m_buffer.line_and_column_at(cursor_iterator());
move_cursor_to(m_buffer.iterator_at(pos + BufferCoord(offset)));
}
else else
{ {
for (auto& sel : m_selections) for (auto& sel : m_selections)
{ {
DisplayCoord pos = line_and_column_at(sel.last()); BufferCoord pos = m_buffer.line_and_column_at(sel.last());
sel = Selection(sel.first(), iterator_at(pos + offset)); sel = Selection(sel.first(), m_buffer.iterator_at(pos + BufferCoord(offset)));
} }
scroll_to_keep_cursor_visible_ifn(); scroll_to_keep_cursor_visible_ifn();
} }
@ -298,7 +328,7 @@ void Window::update_display_buffer()
if (begin == end) if (begin == end)
return; return;
m_display_buffer.append(DisplayAtom(begin, end)); m_display_buffer.append(DisplayAtom(DisplayCoord(0,0), begin, end));
for (auto& filter : m_filters) for (auto& filter : m_filters)
{ {
@ -338,7 +368,7 @@ void Window::scroll_to_keep_cursor_visible_ifn()
std::string Window::status_line() const std::string Window::status_line() const
{ {
BufferCoord cursor = window_to_buffer(cursor_position()); BufferCoord cursor = m_buffer.line_and_column_at(m_selections.back().end());
std::ostringstream oss; std::ostringstream oss;
oss << m_buffer.name(); oss << m_buffer.name();
if (m_buffer.is_modified()) if (m_buffer.is_modified())

View File

@ -43,6 +43,7 @@ public:
const BufferCoord& position() const { return m_position; } const BufferCoord& position() const { return m_position; }
DisplayCoord cursor_position() const; DisplayCoord cursor_position() const;
BufferIterator cursor_iterator() const;
Buffer& buffer() const { return m_buffer; } Buffer& buffer() const { return m_buffer; }
@ -80,9 +81,6 @@ private:
void insert_noundo(const String& string); void insert_noundo(const String& string);
void append_noundo(const String& string); void append_noundo(const String& string);
BufferCoord window_to_buffer(const DisplayCoord& window_pos) const;
DisplayCoord buffer_to_window(const BufferCoord& buffer_pos) const;
friend class IncrementalInserter; friend class IncrementalInserter;
IncrementalInserter* m_current_inserter; IncrementalInserter* m_current_inserter;