Avoid the spurious newline insertion when replacing at end of buffer

Add a Buffer::replace method to handle the replacements properly
Fixes #633
This commit is contained in:
Maxime Coste 2016-03-16 13:48:11 +00:00
parent c5b24e2a8a
commit de1433d30a
7 changed files with 48 additions and 11 deletions

View File

@ -343,8 +343,9 @@ ByteCoord Buffer::do_insert(ByteCoord pos, StringView content)
return pos; return pos;
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');
if (at_end) if (at_end)
pos = line_count(); pos = append_lines ? line_count() : end_coord();
const StringView prefix = at_end ? const StringView prefix = at_end ?
StringView{} : m_lines[pos.line].substr(0, pos.column); StringView{} : m_lines[pos.line].substr(0, pos.column);
@ -369,7 +370,7 @@ ByteCoord Buffer::do_insert(ByteCoord pos, StringView content)
auto line_it = m_lines.begin() + (int)pos.line; auto line_it = m_lines.begin() + (int)pos.line;
auto new_lines_it = new_lines.begin(); auto new_lines_it = new_lines.begin();
if (not at_end) if (not append_lines)
*line_it++ = std::move(*new_lines_it++); *line_it++ = std::move(*new_lines_it++);
m_lines.insert(line_it, m_lines.insert(line_it,
@ -473,6 +474,25 @@ BufferIterator Buffer::erase(BufferIterator begin, BufferIterator end)
return {*this, do_erase(begin.coord(), end.coord())}; return {*this, do_erase(begin.coord(), end.coord())};
} }
BufferIterator Buffer::replace(const BufferIterator& begin, const BufferIterator& end, StringView content)
{
if (not (m_flags & Flags::NoUndo))
m_current_undo_group.emplace_back(Modification::Erase, begin.coord(),
intern(string(begin.coord(), end.coord())));
auto pos = do_erase(begin.coord(), end.coord());
StringDataPtr real_content;
if (is_end(pos) and content.back() != '\n')
real_content = intern(content + "\n");
else
real_content = intern(content);
auto coord = is_end(pos) ? ByteCoord{line_count()} : pos;
if (not (m_flags & Flags::NoUndo))
m_current_undo_group.emplace_back(Modification::Insert, coord, real_content);
return {*this, do_insert(pos, real_content->strview())};
}
bool Buffer::is_modified() const bool Buffer::is_modified() const
{ {
size_t history_cursor_index = m_history_cursor - m_history.begin(); size_t history_cursor_index = m_history_cursor - m_history.begin();

View File

@ -123,6 +123,7 @@ public:
BufferIterator insert(const BufferIterator& pos, StringView content); BufferIterator insert(const BufferIterator& pos, StringView content);
BufferIterator erase(BufferIterator begin, BufferIterator end); BufferIterator erase(BufferIterator begin, BufferIterator end);
BufferIterator replace(const BufferIterator& begin, const BufferIterator& end, StringView content);
size_t timestamp() const; size_t timestamp() const;
timespec fs_timestamp() const; timespec fs_timestamp() const;

View File

@ -21,6 +21,13 @@ inline BufferIterator erase(Buffer& buffer, const Selection& range)
buffer.iterator_at(buffer.char_next(range.max()))); buffer.iterator_at(buffer.char_next(range.max())));
} }
inline BufferIterator replace(Buffer& buffer, const Selection& range, StringView content)
{
return buffer.replace(buffer.iterator_at(range.min()),
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)
{ {
return utf8::distance(buffer.iterator_at(range.min()), return utf8::distance(buffer.iterator_at(range.min()),

View File

@ -439,7 +439,7 @@ BufferIterator prepare_insert(Buffer& buffer, const Selection& sel, InsertMode m
case InsertMode::InsertCursor: case InsertMode::InsertCursor:
return buffer.iterator_at(sel.cursor()); return buffer.iterator_at(sel.cursor());
case InsertMode::Replace: case InsertMode::Replace:
return erase(buffer, sel); 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
@ -482,21 +482,24 @@ void SelectionList::insert(ConstArrayView<String> strings, InsertMode mode,
changes_tracker.update(*m_buffer, m_timestamp); changes_tracker.update(*m_buffer, m_timestamp);
const String& str = strings[std::min(index, strings.size()-1)]; const String& str = strings[std::min(index, strings.size()-1)];
if (str.empty())
{
if (mode == InsertMode::Replace)
sel.anchor() = sel.cursor() = m_buffer->clamp(pos.coord());
continue;
}
if (mode == InsertMode::Replace)
pos = replace(*m_buffer, sel, str);
else
pos = m_buffer->insert(pos, str); pos = m_buffer->insert(pos, str);
auto& change = m_buffer->changes_since(m_timestamp).back(); auto& change = m_buffer->changes_since(m_timestamp).back();
changes_tracker.update(change); changes_tracker.update(*m_buffer, m_timestamp);
m_timestamp = m_buffer->timestamp(); m_timestamp = m_buffer->timestamp();
if (select_inserted or mode == InsertMode::Replace) if (select_inserted or mode == InsertMode::Replace)
{ {
if (str.empty())
{
sel.anchor() = sel.cursor() = m_buffer->clamp(pos.coord());
continue;
}
// we want min and max from *before* we do any change // we want min and max from *before* we do any change
auto& min = sel.min(); auto& min = sel.min();
auto& max = sel.max(); auto& max = sel.max();
@ -505,6 +508,9 @@ void SelectionList::insert(ConstArrayView<String> strings, InsertMode mode,
} }
else else
{ {
if (str.empty())
continue;
sel.anchor() = m_buffer->clamp(update_insert(sel.anchor(), change.begin, change.end)); sel.anchor() = m_buffer->clamp(update_insert(sel.anchor(), change.begin, change.end));
sel.cursor() = m_buffer->clamp(update_insert(sel.cursor(), change.begin, change.end)); sel.cursor() = m_buffer->clamp(update_insert(sel.cursor(), change.begin, change.end));
} }