Fix the Buffer::end() madness

Until now, buffer had multiple recognized end coordinates, either
{ line_count, 0 } or { line_count - 1, line[line_count - 1].length }.

Now the only correct end coord is { line_count, 0 }, removing the need
for various special cases.
This commit is contained in:
Maxime Coste 2017-06-11 12:01:40 +01:00
parent b4647a16dd
commit 63a791d651
5 changed files with 30 additions and 66 deletions

View File

@ -88,7 +88,7 @@ Buffer::Buffer(String name, Flags flags, StringView data,
#endif #endif
static_cast<BufferLines&>(m_lines) = std::move(parsed_lines.lines); static_cast<BufferLines&>(m_lines) = std::move(parsed_lines.lines);
m_changes.push_back({ Change::Insert, true, {0,0}, line_count() }); m_changes.push_back({ Change::Insert, {0,0}, line_count() });
apply_options(options(), parsed_lines); apply_options(options(), parsed_lines);
@ -241,11 +241,11 @@ void Buffer::reload(StringView data, timespec fs_timestamp)
if (not record_undo) if (not record_undo)
{ {
m_changes.push_back({ Change::Erase, true, {0,0}, line_count() }); m_changes.push_back({ Change::Erase, {0,0}, line_count() });
static_cast<BufferLines&>(m_lines) = std::move(parsed_lines.lines); static_cast<BufferLines&>(m_lines) = std::move(parsed_lines.lines);
m_changes.push_back({ Change::Insert, true, {0,0}, line_count() }); m_changes.push_back({ Change::Insert, {0,0}, line_count() });
} }
else else
{ {
@ -268,7 +268,7 @@ void Buffer::reload(StringView data, timespec fs_timestamp)
Modification::Insert, cur_line + line, Modification::Insert, cur_line + line,
parsed_lines.lines[(int)(d.posB + line)]); parsed_lines.lines[(int)(d.posB + line)]);
m_changes.push_back({ Change::Insert, it == m_lines.end(), cur_line, cur_line + d.len }); m_changes.push_back({ Change::Insert, cur_line, cur_line + d.len });
m_lines.insert(it, &parsed_lines.lines[d.posB], &parsed_lines.lines[d.posB + d.len]); m_lines.insert(it, &parsed_lines.lines[d.posB], &parsed_lines.lines[d.posB + d.len]);
it = m_lines.begin() + (int)(cur_line + d.len); it = m_lines.begin() + (int)(cur_line + d.len);
} }
@ -282,7 +282,7 @@ void Buffer::reload(StringView data, timespec fs_timestamp)
m_lines.get_storage(cur_line + line)); m_lines.get_storage(cur_line + line));
it = m_lines.erase(it, it + d.len); it = m_lines.erase(it, it + d.len);
m_changes.push_back({ Change::Erase, it == m_lines.end(), cur_line, cur_line + d.len }); m_changes.push_back({ Change::Erase, cur_line, cur_line + d.len });
} }
} }
} }
@ -453,8 +453,6 @@ BufferCoord Buffer::do_insert(BufferCoord pos, StringView content)
const bool at_end = is_end(pos); const bool at_end = is_end(pos);
const bool append_lines = at_end and (m_lines.empty() or byte_at(back_coord()) == '\n'); const bool append_lines = at_end and (m_lines.empty() or byte_at(back_coord()) == '\n');
if (at_end)
pos = append_lines ? line_count() : end_coord();
const StringView prefix = append_lines ? const StringView prefix = append_lines ?
StringView{} : m_lines[pos.line].substr(0, pos.column); StringView{} : m_lines[pos.line].substr(0, pos.column);
@ -490,7 +488,7 @@ BufferCoord Buffer::do_insert(BufferCoord pos, StringView content)
const auto end = at_end ? line_count() const auto end = at_end ? line_count()
: BufferCoord{ last_line, m_lines[last_line].length() - suffix.length() }; : BufferCoord{ last_line, m_lines[last_line].length() - suffix.length() };
m_changes.push_back({ Change::Insert, at_end, pos, end }); m_changes.push_back({ Change::Insert, pos, end });
return pos; return pos;
} }
@ -502,7 +500,7 @@ BufferCoord Buffer::do_erase(BufferCoord begin, BufferCoord end)
kak_assert(is_valid(begin)); kak_assert(is_valid(begin));
kak_assert(is_valid(end)); kak_assert(is_valid(end));
StringView prefix = m_lines[begin.line].substr(0, begin.column); StringView prefix = m_lines[begin.line].substr(0, begin.column);
StringView suffix = m_lines[end.line].substr(end.column); StringView suffix = end.line == line_count() ? StringView{} : m_lines[end.line].substr(end.column);
BufferCoord next; BufferCoord next;
if (not prefix.empty() or not suffix.empty()) if (not prefix.empty() or not suffix.empty())
@ -514,11 +512,11 @@ BufferCoord Buffer::do_erase(BufferCoord begin, BufferCoord end)
} }
else else
{ {
m_lines.erase(m_lines.begin() + (int)begin.line, m_lines.begin() + (int)end.line + 1); m_lines.erase(m_lines.begin() + (int)begin.line, m_lines.begin() + (int)end.line);
next = is_end(begin) ? end_coord() : BufferCoord{begin.line, 0}; next = begin.line;
} }
m_changes.push_back({ Change::Erase, is_end(begin), begin, end }); m_changes.push_back({ Change::Erase, begin, end });
return next; return next;
} }
@ -584,24 +582,11 @@ BufferCoord Buffer::erase(BufferCoord begin, BufferCoord end)
BufferCoord Buffer::replace(BufferCoord begin, BufferCoord end, StringView content) BufferCoord Buffer::replace(BufferCoord begin, BufferCoord end, StringView content)
{ {
if (not (m_flags & Flags::NoUndo)) if (is_end(end) and not content.empty() and content.back() == '\n')
m_current_undo_group.emplace_back(Modification::Erase, begin, content = content.substr(0, content.length() - 1);
intern(string(begin, end)));
auto pos = do_erase(begin, end);
StringDataPtr real_content; auto pos = erase(begin, end);
if (is_end(pos) and content.back() != '\n') return insert(pos, content);
real_content = intern(content + "\n");
else
real_content = intern(content);
bool last_char_is_eol = not (m_lines.empty() or
m_lines.back().empty() or
m_lines.back().back() != '\n');
auto coord = is_end(pos) and last_char_is_eol ? line_count() : pos;
if (not (m_flags & Flags::NoUndo))
m_current_undo_group.emplace_back(Modification::Insert, coord, real_content);
return do_insert(pos, real_content->strview());
} }
bool Buffer::is_modified() const bool Buffer::is_modified() const
@ -662,8 +647,6 @@ BufferCoord Buffer::char_next(BufferCoord coord) const
coord.column = 0; coord.column = 0;
} }
} }
else if (coord.line == m_lines.size() - 1)
coord.column = m_lines.back().length();
else else
{ {
++coord.line; ++coord.line;
@ -675,9 +658,7 @@ BufferCoord Buffer::char_next(BufferCoord coord) const
BufferCoord Buffer::char_prev(BufferCoord coord) const BufferCoord Buffer::char_prev(BufferCoord coord) const
{ {
kak_assert(is_valid(coord)); kak_assert(is_valid(coord));
if (is_end(coord)) if (coord.column == 0)
return {(int)m_lines.size()-1, m_lines.back().length() - 1};
else if (coord.column == 0)
{ {
if (coord.line > 0) if (coord.line > 0)
coord.column = m_lines[--coord.line].length() - 1; coord.column = m_lines[--coord.line].length() - 1;

View File

@ -89,9 +89,9 @@ public:
private: private:
SafePtr<const Buffer> m_buffer; SafePtr<const Buffer> m_buffer;
StringView m_line;
BufferCoord m_coord; BufferCoord m_coord;
LineCount m_last_line; LineCount m_line_count;
StringView m_line;
}; };
using BufferLines = Vector<StringDataPtr, MemoryDomain::BufferContent>; using BufferLines = Vector<StringDataPtr, MemoryDomain::BufferContent>;
@ -205,7 +205,6 @@ public:
{ {
enum Type : char { Insert, Erase }; enum Type : char { Insert, Erase };
Type type; Type type;
bool at_end;
BufferCoord begin; BufferCoord begin;
BufferCoord end; BufferCoord end;
}; };

View File

@ -17,8 +17,6 @@ inline BufferCoord Buffer::next(BufferCoord coord) const
{ {
if (coord.column < m_lines[coord.line].length() - 1) if (coord.column < m_lines[coord.line].length() - 1)
++coord.column; ++coord.column;
else if (coord.line == m_lines.size() - 1)
coord.column = m_lines.back().length();
else else
{ {
++coord.line; ++coord.line;
@ -59,7 +57,6 @@ inline bool Buffer::is_valid(BufferCoord c) const
return false; return false;
return (c.line < line_count() and c.column < m_lines[c.line].length()) or return (c.line < line_count() and c.column < m_lines[c.line].length()) or
(c.line == line_count() - 1 and c.column == m_lines.back().length()) or
(c.line == line_count() and c.column == 0); (c.line == line_count() and c.column == 0);
} }
@ -110,14 +107,13 @@ inline BufferCoord Buffer::back_coord() const
inline BufferCoord Buffer::end_coord() const inline BufferCoord Buffer::end_coord() const
{ {
return m_lines.empty() ? return line_count();
BufferCoord{0,0} : BufferCoord{ line_count() - 1, m_lines.back().length() };
} }
inline BufferIterator::BufferIterator(const Buffer& buffer, BufferCoord coord) noexcept inline BufferIterator::BufferIterator(const Buffer& buffer, BufferCoord coord) noexcept
: m_buffer(&buffer), m_coord(coord), : m_buffer{&buffer}, m_coord{coord},
m_line((*m_buffer)[coord.line]), m_line_count{buffer.line_count()},
m_last_line(buffer.line_count()-1) {} m_line{coord.line < buffer.line_count() ? (*m_buffer)[coord.line] : StringView{}} {}
inline bool BufferIterator::operator==(const BufferIterator& iterator) const noexcept inline bool BufferIterator::operator==(const BufferIterator& iterator) const noexcept
{ {
@ -197,9 +193,10 @@ inline BufferIterator& BufferIterator::operator-=(ByteCount size)
inline BufferIterator& BufferIterator::operator++() inline BufferIterator& BufferIterator::operator++()
{ {
if (++m_coord.column == m_line.length() and m_coord.line != m_last_line) if (++m_coord.column == m_line.length())
{ {
m_line = (*m_buffer)[++m_coord.line]; m_line = (++m_coord.line < m_line_count) ?
(*m_buffer)[m_coord.line] : StringView{};
m_coord.column = 0; m_coord.column = 0;
} }
return *this; return *this;
@ -207,7 +204,7 @@ inline BufferIterator& BufferIterator::operator++()
inline BufferIterator& BufferIterator::operator--() inline BufferIterator& BufferIterator::operator--()
{ {
if (m_coord.column == 0 and m_coord.line > 0) if (m_coord.column == 0)
{ {
m_line = (*m_buffer)[--m_coord.line]; m_line = (*m_buffer)[--m_coord.line];
m_coord.column = m_line.length() - 1; m_coord.column = m_line.length() - 1;

View File

@ -10,22 +10,11 @@ static LineModification make_line_modif(const Buffer::Change& change)
{ {
LineCount num_added = 0, num_removed = 0; LineCount num_added = 0, num_removed = 0;
if (change.type == Buffer::Change::Insert) if (change.type == Buffer::Change::Insert)
{
num_added = change.end.line - change.begin.line; num_added = change.end.line - change.begin.line;
// inserted a new line at buffer end but end coord is on same line
if (change.at_end and change.end.column != 0)
++num_added;
}
else else
{
num_removed = change.end.line - change.begin.line; num_removed = change.end.line - change.begin.line;
// removed last line, but end coord is on same line
if (change.at_end and change.end.column != 0)
++num_removed;
}
// modified a line // modified a line
if (not change.at_end and if ((change.begin.column != 0 or change.end.column != 0))
(change.begin.column != 0 or change.end.column != 0))
{ {
++num_removed; ++num_removed;
++num_added; ++num_added;
@ -116,7 +105,7 @@ UnitTest test_line_modifications{[]()
{ {
Buffer buffer("test", Buffer::Flags::None, "line 1\nline 2\n"); Buffer buffer("test", Buffer::Flags::None, "line 1\nline 2\n");
auto ts = buffer.timestamp(); auto ts = buffer.timestamp();
buffer.insert({1, 7}, "line 3"); buffer.insert({2, 0}, "line 3");
auto modifs = compute_line_modifications(buffer, ts); auto modifs = compute_line_modifications(buffer, ts);
kak_assert(modifs.size() == 1 and modifs[0] == LineModification{ 2, 2, 0, 1 }); kak_assert(modifs.size() == 1 and modifs[0] == LineModification{ 2, 2, 0, 1 });

View File

@ -448,17 +448,15 @@ void SelectionList::erase()
kak_assert(m_buffer->is_valid(sel.cursor())); kak_assert(m_buffer->is_valid(sel.cursor()));
auto pos = Kakoune::erase(*m_buffer, sel); auto pos = Kakoune::erase(*m_buffer, sel);
sel.anchor() = sel.cursor() = m_buffer->clamp(pos); sel.anchor() = sel.cursor() = pos;
changes_tracker.update(*m_buffer, m_timestamp); changes_tracker.update(*m_buffer, m_timestamp);
} }
BufferCoord back_coord = m_buffer->back_coord(); BufferCoord back_coord = m_buffer->back_coord();
for (auto& sel : m_selections) for (auto& sel : m_selections)
{ {
if (sel.anchor() > back_coord) auto pos = m_buffer->clamp(sel.cursor());
sel.anchor() = back_coord; sel.anchor() = sel.cursor() = std::min(pos, back_coord);
if (sel.cursor() > back_coord)
sel.cursor() = back_coord;
} }
m_buffer->check_invariant(); m_buffer->check_invariant();