Use ByteCoords directly for buffer insert/erase/replace

This commit is contained in:
Maxime Coste 2016-03-16 13:59:30 +00:00
parent ad5da15cfa
commit 131b0a8298
9 changed files with 73 additions and 75 deletions

View File

@ -439,47 +439,49 @@ void Buffer::apply_modification(const Modification& modification)
} }
} }
BufferIterator Buffer::insert(const BufferIterator& pos, StringView content) ByteCoord Buffer::insert(ByteCoord pos, StringView content)
{ {
kak_assert(is_valid(pos.coord())); kak_assert(is_valid(pos));
if (content.empty()) if (content.empty())
return pos; return pos;
StringDataPtr real_content; StringDataPtr real_content;
if (pos == end() and content.back() != '\n') if (is_end(pos) and content.back() != '\n')
real_content = intern(content + "\n"); real_content = intern(content + "\n");
else else
real_content = intern(content); real_content = intern(content);
// for undo and redo purpose it is better to use one past last line rather // for undo and redo purpose it is better to use one past last line rather
// than one past last char coord. // than one past last char coord.
auto coord = pos == end() ? ByteCoord{line_count()} : pos.coord(); auto coord = is_end(pos) ? ByteCoord{line_count()} : pos;
if (not (m_flags & Flags::NoUndo)) if (not (m_flags & Flags::NoUndo))
m_current_undo_group.emplace_back(Modification::Insert, coord, real_content); m_current_undo_group.emplace_back(Modification::Insert, coord, real_content);
return {*this, do_insert(pos.coord(), real_content->strview())}; return do_insert(pos, real_content->strview());
} }
BufferIterator Buffer::erase(BufferIterator begin, BufferIterator end) ByteCoord Buffer::erase(ByteCoord begin, ByteCoord end)
{ {
// do not erase last \n except if we erase from the start of a line kak_assert(is_valid(begin) and is_valid(end));
if (end == this->end() and (begin.coord().column != 0 or begin == this->begin())) // do not erase last \n except if we erase from the start of a line, and normalize
--end; // end coord
if (is_end(end))
end = (begin.column != 0 or begin == ByteCoord{0,0}) ? prev(end) : end_coord();
if (begin == end) if (begin >= end) // use >= to handle case where begin is {line_count}
return begin; return begin;
if (not (m_flags & Flags::NoUndo)) if (not (m_flags & Flags::NoUndo))
m_current_undo_group.emplace_back(Modification::Erase, begin.coord(), m_current_undo_group.emplace_back(Modification::Erase, begin,
intern(string(begin.coord(), end.coord()))); intern(string(begin, end)));
return {*this, do_erase(begin.coord(), end.coord())}; return do_erase(begin, end);
} }
BufferIterator Buffer::replace(const BufferIterator& begin, const BufferIterator& end, StringView content) ByteCoord Buffer::replace(ByteCoord begin, ByteCoord end, StringView content)
{ {
if (not (m_flags & Flags::NoUndo)) if (not (m_flags & Flags::NoUndo))
m_current_undo_group.emplace_back(Modification::Erase, begin.coord(), m_current_undo_group.emplace_back(Modification::Erase, begin,
intern(string(begin.coord(), end.coord()))); intern(string(begin, end)));
auto pos = do_erase(begin.coord(), end.coord()); auto pos = do_erase(begin, end);
StringDataPtr real_content; StringDataPtr real_content;
if (is_end(pos) and content.back() != '\n') if (is_end(pos) and content.back() != '\n')
@ -490,7 +492,7 @@ BufferIterator Buffer::replace(const BufferIterator& begin, const BufferIterator
auto coord = is_end(pos) ? ByteCoord{line_count()} : pos; auto coord = is_end(pos) ? ByteCoord{line_count()} : pos;
if (not (m_flags & Flags::NoUndo)) if (not (m_flags & Flags::NoUndo))
m_current_undo_group.emplace_back(Modification::Insert, coord, real_content); m_current_undo_group.emplace_back(Modification::Insert, coord, real_content);
return {*this, do_insert(pos, real_content->strview())}; return do_insert(pos, real_content->strview());
} }
bool Buffer::is_modified() const bool Buffer::is_modified() const
@ -650,7 +652,7 @@ UnitTest test_buffer{[]()
kak_assert(pos.coord() == ByteCoord{0 COMMA 6}); kak_assert(pos.coord() == ByteCoord{0 COMMA 6});
pos += 1; pos += 1;
kak_assert(pos.coord() == ByteCoord{1 COMMA 0}); kak_assert(pos.coord() == ByteCoord{1 COMMA 0});
buffer.insert(pos, "tchou kanaky\n"); buffer.insert(pos.coord(), "tchou kanaky\n");
kak_assert(buffer.line_count() == 5); kak_assert(buffer.line_count() == 5);
BufferIterator pos2 = buffer.end(); BufferIterator pos2 = buffer.end();
pos2 -= 9; pos2 -= 9;
@ -661,16 +663,16 @@ UnitTest test_buffer{[]()
// check insert at end behaviour: auto add end of line if necessary // check insert at end behaviour: auto add end of line if necessary
pos = buffer.end()-1; pos = buffer.end()-1;
buffer.insert(pos, "tchou"); buffer.insert(pos.coord(), "tchou");
kak_assert(buffer.string(pos.coord(), buffer.end_coord()) == StringView{"tchou\n"}); kak_assert(buffer.string(pos.coord(), buffer.end_coord()) == StringView{"tchou\n"});
pos = buffer.end()-1; pos = buffer.end()-1;
buffer.insert(buffer.end(), "kanaky\n"); buffer.insert(buffer.end_coord(), "kanaky\n");
kak_assert(buffer.string((pos+1).coord(), buffer.end_coord()) == StringView{"kanaky\n"}); kak_assert(buffer.string((pos+1).coord(), buffer.end_coord()) == StringView{"kanaky\n"});
buffer.commit_undo_group(); buffer.commit_undo_group();
buffer.erase(pos+1, buffer.end()); buffer.erase((pos+1).coord(), buffer.end_coord());
buffer.insert(buffer.end(), "mutch\n"); buffer.insert(buffer.end_coord(), "mutch\n");
buffer.commit_undo_group(); buffer.commit_undo_group();
buffer.undo(); buffer.undo();
kak_assert(buffer.string(buffer.advance(buffer.end_coord(), -7), buffer.end_coord()) == StringView{"kanaky\n"}); kak_assert(buffer.string(buffer.advance(buffer.end_coord(), -7), buffer.end_coord()) == StringView{"kanaky\n"});
@ -681,12 +683,12 @@ UnitTest test_buffer{[]()
UnitTest test_undo{[]() UnitTest test_undo{[]()
{ {
Buffer buffer("test", Buffer::Flags::None, "allo ?\nmais que fais la police\n hein ?\n youpi\n"); Buffer buffer("test", Buffer::Flags::None, "allo ?\nmais que fais la police\n hein ?\n youpi\n");
auto pos = buffer.insert(buffer.end(), "kanaky\n"); auto pos = buffer.insert(buffer.end_coord(), "kanaky\n");
buffer.erase(pos, buffer.end()); buffer.erase(pos, buffer.end_coord());
buffer.insert(buffer.iterator_at(2_line), "tchou\n"); buffer.insert(2_line, "tchou\n");
buffer.insert(buffer.iterator_at(2_line), "mutch\n"); buffer.insert(2_line, "mutch\n");
buffer.erase(buffer.iterator_at({2, 1}), buffer.iterator_at({2, 5})); buffer.erase({2, 1}, {2, 5});
buffer.replace(buffer.iterator_at(2_line), buffer.end(), "youpi"); buffer.replace(2_line, buffer.end_coord(), "youpi");
buffer.undo(); buffer.undo();
buffer.redo(); buffer.redo();
buffer.undo(); buffer.undo();

View File

@ -121,9 +121,9 @@ public:
bool set_name(String name); bool set_name(String name);
void update_display_name(); void update_display_name();
BufferIterator insert(const BufferIterator& pos, StringView content); ByteCoord insert(ByteCoord pos, StringView content);
BufferIterator erase(BufferIterator begin, BufferIterator end); ByteCoord erase(ByteCoord begin, ByteCoord end);
BufferIterator replace(const BufferIterator& begin, const BufferIterator& end, StringView content); ByteCoord replace(ByteCoord begin, ByteCoord end, StringView content);
size_t timestamp() const; size_t timestamp() const;
timespec fs_timestamp() const; timespec fs_timestamp() const;

View File

@ -122,21 +122,21 @@ Buffer* create_fifo_buffer(String name, int fd, bool scroll)
do do
{ {
count = read(fifo, data, buffer_size); count = read(fifo, data, buffer_size);
auto pos = buffer->end()-1; auto pos = buffer->back_coord();
bool prevent_scrolling = pos == buffer->begin() and not scroll; const bool prevent_scrolling = pos == ByteCoord{0,0} and not scroll;
if (prevent_scrolling) if (prevent_scrolling)
++pos; pos = buffer->next(pos);
buffer->insert(pos, StringView(data, data+count)); buffer->insert(pos, StringView(data, data+count));
if (count > 0 and prevent_scrolling) if (count > 0 and prevent_scrolling)
{ {
buffer->erase(buffer->begin(), buffer->begin()+1); buffer->erase({0,0}, buffer->next({0,0}));
// in the other case, the buffer will have automatically // in the other case, the buffer will have automatically
// inserted a \n to guarantee its invariant. // inserted a \n to guarantee its invariant.
if (data[count-1] == '\n') if (data[count-1] == '\n')
buffer->insert(buffer->end(), "\n"); buffer->insert(buffer->end_coord(), "\n");
} }
FD_ZERO(&rfds); FD_ZERO(&rfds);
@ -172,7 +172,7 @@ void write_to_debug_buffer(StringView str)
// where the user can put its cursor to scroll with new messages // where the user can put its cursor to scroll with new messages
const bool eol_back = not str.empty() and str.back() == '\n'; const bool eol_back = not str.empty() and str.back() == '\n';
if (Buffer* buffer = BufferManager::instance().get_buffer_ifp(debug_buffer_name)) if (Buffer* buffer = BufferManager::instance().get_buffer_ifp(debug_buffer_name))
buffer->insert(buffer->end()-1, eol_back ? str : str + "\n"); buffer->insert(buffer->back_coord(), eol_back ? str : str + "\n");
else else
{ {
String line = str + (eol_back ? "\n" : "\n\n"); String line = str + (eol_back ? "\n" : "\n\n");

View File

@ -15,17 +15,14 @@ inline String content(const Buffer& buffer, const Selection& range)
return buffer.string(range.min(), buffer.char_next(range.max())); return buffer.string(range.min(), buffer.char_next(range.max()));
} }
inline BufferIterator erase(Buffer& buffer, const Selection& range) inline ByteCoord erase(Buffer& buffer, const Selection& range)
{ {
return buffer.erase(buffer.iterator_at(range.min()), return buffer.erase(range.min(), buffer.char_next(range.max()));
buffer.iterator_at(buffer.char_next(range.max())));
} }
inline BufferIterator replace(Buffer& buffer, const Selection& range, StringView content) inline ByteCoord replace(Buffer& buffer, const Selection& range, StringView content)
{ {
return buffer.replace(buffer.iterator_at(range.min()), return buffer.replace(range.min(), buffer.char_next(range.max()), content);
buffer.iterator_at(buffer.char_next(range.max())),
content);
} }
inline CharCount char_length(const Buffer& buffer, const Selection& range) inline CharCount char_length(const Buffer& buffer, const Selection& range)

View File

@ -350,8 +350,8 @@ void InsertCompleter::select(int offset, Vector<Key>& keystrokes)
if (cursor.column >= prefix_len and (pos + suffix_len) != buffer.end() and if (cursor.column >= prefix_len and (pos + suffix_len) != buffer.end() and
std::equal(ref.begin(), ref.end(), pos - prefix_len)) std::equal(ref.begin(), ref.end(), pos - prefix_len))
{ {
pos = buffer.erase(pos - prefix_len, pos + suffix_len); buffer.replace((pos - prefix_len).coord(),
buffer.insert(pos, candidate.completion); (pos + suffix_len).coord(), candidate.completion);
const_cast<SelectionList&>(selections).update(); const_cast<SelectionList&>(selections).update();
} }
} }

View File

@ -107,7 +107,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.erase(buffer.iterator_at({1, 0}), buffer.iterator_at({2, 0})); buffer.erase({1, 0}, {2, 0});
auto modifs = compute_line_modifications(buffer, ts); auto modifs = compute_line_modifications(buffer, ts);
kak_assert(modifs.size() == 1 and modifs[0] == LineModification{ 1 COMMA 1 COMMA 1 COMMA 0 }); kak_assert(modifs.size() == 1 and modifs[0] == LineModification{ 1 COMMA 1 COMMA 1 COMMA 0 });
@ -116,7 +116,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(buffer.iterator_at({1, 7}), "line 3"); buffer.insert({1, 7}, "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 COMMA 2 COMMA 0 COMMA 1 }); kak_assert(modifs.size() == 1 and modifs[0] == LineModification{ 2 COMMA 2 COMMA 0 COMMA 1 });
@ -126,8 +126,8 @@ UnitTest test_line_modifications{[]()
Buffer buffer("test", Buffer::Flags::None, "line 1\nline 2\nline 3\n"); Buffer buffer("test", Buffer::Flags::None, "line 1\nline 2\nline 3\n");
auto ts = buffer.timestamp(); auto ts = buffer.timestamp();
buffer.insert(buffer.iterator_at({1, 4}), "hoho\nhehe"); buffer.insert({1, 4}, "hoho\nhehe");
buffer.erase(buffer.iterator_at({0, 0}), buffer.iterator_at({1, 0})); buffer.erase({0, 0}, {1, 0});
auto modifs = compute_line_modifications(buffer, ts); auto modifs = compute_line_modifications(buffer, ts);
kak_assert(modifs.size() == 1 and modifs[0] == LineModification{ 0 COMMA 0 COMMA 2 COMMA 2 }); kak_assert(modifs.size() == 1 and modifs[0] == LineModification{ 0 COMMA 0 COMMA 2 COMMA 2 });
@ -137,14 +137,14 @@ UnitTest test_line_modifications{[]()
Buffer buffer("test", Buffer::Flags::None, "line 1\nline 2\nline 3\nline 4\n"); Buffer buffer("test", Buffer::Flags::None, "line 1\nline 2\nline 3\nline 4\n");
auto ts = buffer.timestamp(); auto ts = buffer.timestamp();
buffer.erase(buffer.iterator_at({0,0}), buffer.iterator_at({3,0})); buffer.erase({0,0}, {3,0});
buffer.insert(buffer.iterator_at({1,0}), "newline 1\nnewline 2\nnewline 3\n"); buffer.insert({1,0}, "newline 1\nnewline 2\nnewline 3\n");
buffer.erase(buffer.iterator_at({0,0}), buffer.iterator_at({1,0})); buffer.erase({0,0}, {1,0});
{ {
auto modifs = compute_line_modifications(buffer, ts); auto modifs = compute_line_modifications(buffer, ts);
kak_assert(modifs.size() == 1 and modifs[0] == LineModification{ 0 COMMA 0 COMMA 4 COMMA 3 }); kak_assert(modifs.size() == 1 and modifs[0] == LineModification{ 0 COMMA 0 COMMA 4 COMMA 3 });
} }
buffer.insert(buffer.iterator_at({3,0}), "newline 4\n"); buffer.insert({3,0}, "newline 4\n");
{ {
auto modifs = compute_line_modifications(buffer, ts); auto modifs = compute_line_modifications(buffer, ts);
@ -155,9 +155,9 @@ UnitTest test_line_modifications{[]()
{ {
Buffer buffer("test", Buffer::Flags::None, "line 1\n"); Buffer buffer("test", Buffer::Flags::None, "line 1\n");
auto ts = buffer.timestamp(); auto ts = buffer.timestamp();
buffer.insert(buffer.iterator_at({0,0}), "n"); buffer.insert({0,0}, "n");
buffer.insert(buffer.iterator_at({0,1}), "e"); buffer.insert({0,1}, "e");
buffer.insert(buffer.iterator_at({0,2}), "w"); buffer.insert({0,2}, "w");
auto modifs = compute_line_modifications(buffer, ts); auto modifs = compute_line_modifications(buffer, ts);
kak_assert(modifs.size() == 1 and modifs[0] == LineModification{ 0 COMMA 0 COMMA 1 COMMA 1 }); kak_assert(modifs.size() == 1 and modifs[0] == LineModification{ 0 COMMA 0 COMMA 1 COMMA 1 });
} }

View File

@ -1244,7 +1244,7 @@ void align(Context& context, NormalParams)
auto spaces = targetcol - (tabs ? (tabcol + tabs * tabstop) : inscol); auto spaces = targetcol - (tabs ? (tabcol + tabs * tabstop) : inscol);
padstr = String{ '\t', tabs } + String{ ' ', spaces }; padstr = String{ '\t', tabs } + String{ ' ', spaces };
} }
buffer.insert(buffer.iterator_at(insert_coord), std::move(padstr)); buffer.insert(insert_coord, std::move(padstr));
} }
selections.update(); selections.update();
} }
@ -1283,8 +1283,7 @@ void copy_indent(Context& context, NormalParams params)
ByteCount i = 0; ByteCount i = 0;
while (i < line.length() and is_horizontal_blank(line[i])) while (i < line.length() and is_horizontal_blank(line[i]))
++i; ++i;
buffer.erase(buffer.iterator_at(l), buffer.iterator_at({l, i})); buffer.replace(l, {l, i}, indent);
buffer.insert(buffer.iterator_at(l), indent);
} }
} }

View File

@ -430,32 +430,32 @@ void SelectionList::avoid_eol()
} }
} }
BufferIterator prepare_insert(Buffer& buffer, const Selection& sel, InsertMode mode) ByteCoord prepare_insert(Buffer& buffer, const Selection& sel, InsertMode mode)
{ {
switch (mode) switch (mode)
{ {
case InsertMode::Insert: case InsertMode::Insert:
return buffer.iterator_at(sel.min()); return sel.min();
case InsertMode::InsertCursor: case InsertMode::InsertCursor:
return buffer.iterator_at(sel.cursor()); return sel.cursor();
case InsertMode::Replace: case InsertMode::Replace:
return {}; // replace is handled specially, by calling Buffer::replace return {}; // replace is handled specially, by calling Buffer::replace
case InsertMode::Append: case InsertMode::Append:
{ {
// special case for end of lines, append to current line instead // special case for end of lines, append to current line instead
auto pos = buffer.iterator_at(sel.max()); auto pos = sel.max();
return *pos == '\n' ? pos : utf8::next(pos, buffer.end()); return buffer.byte_at(pos) == '\n' ? pos : buffer.char_next(pos);
} }
case InsertMode::InsertAtLineBegin: case InsertMode::InsertAtLineBegin:
return buffer.iterator_at(sel.min().line); return sel.min().line;
case InsertMode::AppendAtLineEnd: case InsertMode::AppendAtLineEnd:
return buffer.iterator_at({sel.max().line, buffer[sel.max().line].length() - 1}); return {sel.max().line, buffer[sel.max().line].length() - 1};
case InsertMode::InsertAtNextLineBegin: case InsertMode::InsertAtNextLineBegin:
return buffer.iterator_at(sel.max().line+1); return sel.max().line+1;
case InsertMode::OpenLineBelow: case InsertMode::OpenLineBelow:
return buffer.insert(buffer.iterator_at(sel.max().line + 1), "\n"); return buffer.insert(sel.max().line + 1, "\n");
case InsertMode::OpenLineAbove: case InsertMode::OpenLineAbove:
return buffer.insert(buffer.iterator_at(sel.min().line), "\n"); return buffer.insert(sel.min().line, "\n");
} }
kak_assert(false); kak_assert(false);
return {}; return {};
@ -496,7 +496,7 @@ void SelectionList::insert(ConstArrayView<String> strings, InsertMode mode,
{ {
if (str.empty()) if (str.empty())
{ {
sel.anchor() = sel.cursor() = m_buffer->clamp(pos.coord()); sel.anchor() = sel.cursor() = m_buffer->clamp(pos);
continue; continue;
} }
@ -535,7 +535,7 @@ 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.coord()); sel.anchor() = sel.cursor() = m_buffer->clamp(pos);
changes_tracker.update(*m_buffer, m_timestamp); changes_tracker.update(*m_buffer, m_timestamp);
} }

View File

@ -209,11 +209,11 @@ UnitTest test_word_db{[]()
kak_assert(eq(res, WordList{ "allo" COMMA "kanaky" COMMA "mutch" COMMA "tchaa" COMMA "tchou" })); kak_assert(eq(res, WordList{ "allo" COMMA "kanaky" COMMA "mutch" COMMA "tchaa" COMMA "tchou" }));
kak_assert(word_db.get_word_occurences("tchou") == 3); kak_assert(word_db.get_word_occurences("tchou") == 3);
kak_assert(word_db.get_word_occurences("allo") == 1); kak_assert(word_db.get_word_occurences("allo") == 1);
buffer.erase(buffer.iterator_at({1, 6}), buffer.iterator_at({4, 0})); buffer.erase({1, 6}, {4, 0});
res = word_db.find_matching(""); res = word_db.find_matching("");
std::sort(res.begin(), res.end(), cmp_words); std::sort(res.begin(), res.end(), cmp_words);
kak_assert(eq(res, WordList{ "allo" COMMA "mutch" COMMA "tchou" })); kak_assert(eq(res, WordList{ "allo" COMMA "mutch" COMMA "tchou" }));
buffer.insert(buffer.iterator_at({1, 0}), "re"); buffer.insert({1, 0}, "re");
res = word_db.find_matching(""); res = word_db.find_matching("");
std::sort(res.begin(), res.end(), cmp_words); std::sort(res.begin(), res.end(), cmp_words);
kak_assert(eq(res, WordList{ "allo" COMMA "mutch" COMMA "retchou" COMMA "tchou" })); kak_assert(eq(res, WordList{ "allo" COMMA "mutch" COMMA "retchou" COMMA "tchou" }));