Cleanup replaced range selection logic
Do not access Buffer::m_changes to find the inserted range, return it directly from Buffer::insert and Buffer::replace. This fixes a wrong behaviour where replacing at eof would lose the selected end of line (as the implementation does not actually replace that end of line)
This commit is contained in:
parent
cbb7f601f6
commit
b68490ef11
|
@ -444,12 +444,12 @@ void Buffer::check_invariant() const
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
BufferCoord Buffer::do_insert(BufferCoord pos, StringView content)
|
BufferRange Buffer::do_insert(BufferCoord pos, StringView content)
|
||||||
{
|
{
|
||||||
kak_assert(is_valid(pos));
|
kak_assert(is_valid(pos));
|
||||||
|
|
||||||
if (content.empty())
|
if (content.empty())
|
||||||
return pos;
|
return {pos, 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');
|
const bool append_lines = at_end and (m_lines.empty() or byte_at(back_coord()) == '\n');
|
||||||
|
@ -489,7 +489,7 @@ BufferCoord Buffer::do_insert(BufferCoord pos, StringView content)
|
||||||
: 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, pos, end });
|
m_changes.push_back({ Change::Insert, pos, end });
|
||||||
return end;
|
return {pos, end};
|
||||||
}
|
}
|
||||||
|
|
||||||
BufferCoord Buffer::do_erase(BufferCoord begin, BufferCoord end)
|
BufferCoord Buffer::do_erase(BufferCoord begin, BufferCoord end)
|
||||||
|
@ -543,13 +543,13 @@ void Buffer::apply_modification(const Modification& modification)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BufferCoord Buffer::insert(BufferCoord pos, StringView content)
|
BufferRange Buffer::insert(BufferCoord pos, StringView content)
|
||||||
{
|
{
|
||||||
throw_if_read_only();
|
throw_if_read_only();
|
||||||
|
|
||||||
kak_assert(is_valid(pos));
|
kak_assert(is_valid(pos));
|
||||||
if (content.empty())
|
if (content.empty())
|
||||||
return pos;
|
return {pos, pos};
|
||||||
|
|
||||||
StringDataPtr real_content;
|
StringDataPtr real_content;
|
||||||
if (is_end(pos) and content.back() != '\n')
|
if (is_end(pos) and content.back() != '\n')
|
||||||
|
@ -584,21 +584,20 @@ BufferCoord Buffer::erase(BufferCoord begin, BufferCoord end)
|
||||||
return do_erase(begin, end);
|
return do_erase(begin, end);
|
||||||
}
|
}
|
||||||
|
|
||||||
BufferCoord Buffer::replace(BufferCoord begin, BufferCoord end, StringView content)
|
BufferRange Buffer::replace(BufferCoord begin, BufferCoord end, StringView content)
|
||||||
{
|
{
|
||||||
throw_if_read_only();
|
throw_if_read_only();
|
||||||
|
|
||||||
|
if (std::equal(iterator_at(begin), iterator_at(end), content.begin(), content.end()))
|
||||||
|
return {begin, end};
|
||||||
|
|
||||||
if (is_end(end) and not content.empty() and content.back() == '\n')
|
if (is_end(end) and not content.empty() and content.back() == '\n')
|
||||||
{
|
{
|
||||||
end = back_coord();
|
auto pos = insert(erase(begin, back_coord()),
|
||||||
content = content.substr(0, content.length() - 1);
|
content.substr(0, content.length() - 1)).begin;
|
||||||
|
return {pos, end_coord()};
|
||||||
}
|
}
|
||||||
|
return insert(erase(begin, end), content);
|
||||||
if (std::equal(iterator_at(begin), iterator_at(end), content.begin(), content.end()))
|
|
||||||
return begin;
|
|
||||||
|
|
||||||
auto pos = erase(begin, end);
|
|
||||||
return insert(pos, content);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Buffer::is_modified() const
|
bool Buffer::is_modified() const
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
#include "enum.hh"
|
#include "enum.hh"
|
||||||
#include "file.hh"
|
#include "file.hh"
|
||||||
#include "optional.hh"
|
#include "optional.hh"
|
||||||
|
#include "range.hh"
|
||||||
#include "safe_ptr.hh"
|
#include "safe_ptr.hh"
|
||||||
#include "scope.hh"
|
#include "scope.hh"
|
||||||
#include "shared_string.hh"
|
#include "shared_string.hh"
|
||||||
|
@ -103,6 +104,7 @@ private:
|
||||||
};
|
};
|
||||||
|
|
||||||
using BufferLines = Vector<StringDataPtr, MemoryDomain::BufferContent>;
|
using BufferLines = Vector<StringDataPtr, MemoryDomain::BufferContent>;
|
||||||
|
using BufferRange = Range<BufferCoord>;
|
||||||
|
|
||||||
// A Buffer is a in-memory representation of a file
|
// A Buffer is a in-memory representation of a file
|
||||||
//
|
//
|
||||||
|
@ -139,9 +141,9 @@ public:
|
||||||
bool set_name(String name);
|
bool set_name(String name);
|
||||||
void update_display_name();
|
void update_display_name();
|
||||||
|
|
||||||
BufferCoord insert(BufferCoord pos, StringView content);
|
BufferRange insert(BufferCoord pos, StringView content);
|
||||||
BufferCoord erase(BufferCoord begin, BufferCoord end);
|
BufferCoord erase(BufferCoord begin, BufferCoord end);
|
||||||
BufferCoord replace(BufferCoord begin, BufferCoord end, StringView content);
|
BufferRange replace(BufferCoord begin, BufferCoord end, StringView content);
|
||||||
|
|
||||||
size_t timestamp() const;
|
size_t timestamp() const;
|
||||||
void set_fs_status(FsStatus);
|
void set_fs_status(FsStatus);
|
||||||
|
@ -231,7 +233,7 @@ public:
|
||||||
private:
|
private:
|
||||||
void on_option_changed(const Option& option) override;
|
void on_option_changed(const Option& option) override;
|
||||||
|
|
||||||
BufferCoord do_insert(BufferCoord pos, StringView content);
|
BufferRange do_insert(BufferCoord pos, StringView content);
|
||||||
BufferCoord do_erase(BufferCoord begin, BufferCoord end);
|
BufferCoord do_erase(BufferCoord begin, BufferCoord end);
|
||||||
|
|
||||||
struct Modification;
|
struct Modification;
|
||||||
|
|
|
@ -20,7 +20,7 @@ inline BufferCoord erase(Buffer& buffer, const Selection& range)
|
||||||
return buffer.erase(range.min(), buffer.char_next(range.max()));
|
return buffer.erase(range.min(), buffer.char_next(range.max()));
|
||||||
}
|
}
|
||||||
|
|
||||||
inline BufferCoord replace(Buffer& buffer, const Selection& range, StringView content)
|
inline BufferRange replace(Buffer& buffer, const Selection& range, StringView content)
|
||||||
{
|
{
|
||||||
return buffer.replace(range.min(), buffer.char_next(range.max()), content);
|
return buffer.replace(range.min(), buffer.char_next(range.max()), content);
|
||||||
}
|
}
|
||||||
|
|
|
@ -524,7 +524,7 @@ BufferCoord apply_diff(Buffer& buffer, BufferCoord pos, StringView before, Strin
|
||||||
break;
|
break;
|
||||||
case DiffOp::Add:
|
case DiffOp::Add:
|
||||||
pos = buffer.insert(pos, {lines_after[posB].begin(),
|
pos = buffer.insert(pos, {lines_after[posB].begin(),
|
||||||
lines_after[posB + len - 1].end()});
|
lines_after[posB + len - 1].end()}).end;
|
||||||
break;
|
break;
|
||||||
case DiffOp::Remove:
|
case DiffOp::Remove:
|
||||||
pos = buffer.erase(pos, buffer.advance(pos, byte_count(lines_before, posA, len)));
|
pos = buffer.erase(pos, buffer.advance(pos, byte_count(lines_before, posA, len)));
|
||||||
|
|
|
@ -407,38 +407,24 @@ void SelectionList::insert(ConstArrayView<String> strings, InsertMode mode,
|
||||||
sel.min() : changes_tracker.get_new_coord(insert_pos[index]);
|
sel.min() : changes_tracker.get_new_coord(insert_pos[index]);
|
||||||
|
|
||||||
if (mode == InsertMode::Replace)
|
if (mode == InsertMode::Replace)
|
||||||
replace(*m_buffer, sel, str);
|
{
|
||||||
|
auto range = replace(*m_buffer, sel, str);
|
||||||
|
// we want min and max from *before* we do any change
|
||||||
|
auto& min = sel.min();
|
||||||
|
auto& max = sel.max();
|
||||||
|
min = range.begin;
|
||||||
|
max = m_buffer->char_prev(range.end);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
m_buffer->insert(pos, str);
|
{
|
||||||
|
auto range = m_buffer->insert(pos, str);
|
||||||
|
sel.anchor() = m_buffer->clamp(update_insert(sel.anchor(), range.begin, range.end));
|
||||||
|
sel.cursor() = m_buffer->clamp(update_insert(sel.cursor(), range.begin, range.end));
|
||||||
|
}
|
||||||
|
|
||||||
size_t old_timestamp = m_timestamp;
|
|
||||||
changes_tracker.update(*m_buffer, m_timestamp);
|
changes_tracker.update(*m_buffer, m_timestamp);
|
||||||
|
|
||||||
if (out_insert_pos)
|
if (out_insert_pos)
|
||||||
out_insert_pos->push_back(pos);
|
out_insert_pos->push_back(pos);
|
||||||
|
|
||||||
if (mode == InsertMode::Replace)
|
|
||||||
{
|
|
||||||
auto changes = m_buffer->changes_since(old_timestamp);
|
|
||||||
if (changes.size() == 1) // Nothing got inserted, either str was empty, or just \n at end of buffer
|
|
||||||
sel.anchor() = sel.cursor() = m_buffer->clamp(pos);
|
|
||||||
else if (changes.size() == 2)
|
|
||||||
{
|
|
||||||
// we want min and max from *before* we do any change
|
|
||||||
auto& min = sel.min();
|
|
||||||
auto& max = sel.max();
|
|
||||||
min = changes.back().begin;
|
|
||||||
max = m_buffer->char_prev(changes.back().end);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
kak_assert(changes.empty());
|
|
||||||
}
|
|
||||||
else if (not str.empty())
|
|
||||||
{
|
|
||||||
auto& change = m_buffer->changes_since(0).back();
|
|
||||||
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));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// We might just have been deleting text if strings were empty,
|
// We might just have been deleting text if strings were empty,
|
||||||
|
|
1
test/regression/0-rotate-at-eof-mutates-selection/cmd
Normal file
1
test/regression/0-rotate-at-eof-mutates-selection/cmd
Normal file
|
@ -0,0 +1 @@
|
||||||
|
%<a-s><a-)><a-)>
|
3
test/regression/0-rotate-at-eof-mutates-selection/in
Normal file
3
test/regression/0-rotate-at-eof-mutates-selection/in
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
foo
|
||||||
|
bar
|
||||||
|
baz
|
3
test/regression/0-rotate-at-eof-mutates-selection/out
Normal file
3
test/regression/0-rotate-at-eof-mutates-selection/out
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
bar
|
||||||
|
baz
|
||||||
|
foo
|
Loading…
Reference in New Issue
Block a user