Skip to content

Commit ba41928

Browse files
krobelusmawww
authored andcommitted
Remove spurious newline when | replaces at buffer end
My kak -e "exec %{%ca<ret>b<esc>%|printf '\n\n'<ret>}" adds a spurious third line. When we replace up to the end everything, we keep around a single newline to uphold the buffer invariant, but that newline Commit de1433d (Avoid the spurious newline insertion when replacing at end of buffer, 2016-03-16) fixed an issue for most commands that replace until the buffer end. A similar issue exists for the "|" command. It triggers in fewer cases because the replacement is implemented by applying edits computed by the diff algorithm. It does trigger when "|" replaces the entire buffer. Fix that by erasing the spurious newline. Alternatively, we could allow the diff steps of kind "remove" to delete the entire buffer, and only restore the invariant after the whole diff is applied. This should work because the one-past-end position is valid for Buffer::insert() even if the buffer is empty. It is not valid for Buffer::erase() or Buffer::advance() where count>0 but if we do that when we're already at the buffer end, that is probably a bug in the diff. I tried this but ran into a assertion in ForwardChangesTracker (kak_assert(change.begin >= cur_pos)). Alternatively, we could change the diff algorithm to always insert before deleting. I encountered this issue when using ansi-enable on a fifo buffer. Specifically, the first BufReadFifo hook would replace the entire inserted text but leave around a spurious newline.
1 parent cff5f12 commit ba41928

File tree

4 files changed

+25
-6
lines changed

4 files changed

+25
-6
lines changed

src/normal.cc

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include "changes.hh"
77
#include "command_manager.hh"
88
#include "context.hh"
9+
#include "coord.hh"
910
#include "diff.hh"
1011
#include "enum.hh"
1112
#include "face_registry.hh"
@@ -537,15 +538,17 @@ void command(Context& context, NormalParams params)
537538
command(context, std::move(env_vars), params.reg);
538539
}
539540

540-
BufferCoord apply_diff(Buffer& buffer, BufferCoord pos, ArrayView<StringView> lines_before, StringView after)
541+
BufferRange apply_diff(Buffer& buffer, BufferCoord pos, ArrayView<StringView> lines_before, StringView after)
541542
{
543+
BufferCoord first = pos;
542544
const auto lines_after = after | split_after<StringView>('\n') | gather<Vector<StringView>>();
543545

544546
auto byte_count = [](auto&& lines, int first, int count) {
545547
return std::accumulate(lines.begin() + first, lines.begin() + first + count, 0_byte,
546548
[](ByteCount l, StringView s) { return l + s.length(); });
547549
};
548550

551+
bool tried_to_erase_final_newline = false;
549552
for_each_diff(lines_before.begin(), (int)lines_before.size(),
550553
lines_after.begin(), (int)lines_after.size(),
551554
[&, posA = 0, posB = 0](DiffOp op, int len) mutable {
@@ -557,17 +560,28 @@ BufferCoord apply_diff(Buffer& buffer, BufferCoord pos, ArrayView<StringView> li
557560
posB += len;
558561
break;
559562
case DiffOp::Add:
563+
if (buffer.is_end(pos) and tried_to_erase_final_newline)
564+
pos = buffer.prev(pos);
560565
pos = buffer.insert(pos, {lines_after[posB].begin(),
561566
lines_after[posB + len - 1].end()}).end;
562567
posB += len;
563568
break;
564569
case DiffOp::Remove:
565-
pos = buffer.erase(pos, buffer.advance(pos, byte_count(lines_before, posA, len)));
570+
{
571+
BufferCoord end = buffer.advance(pos, byte_count(lines_before, posA, len));
572+
tried_to_erase_final_newline |= buffer.is_end(end);
573+
pos = buffer.erase(pos, end);
566574
posA += len;
567575
break;
568576
}
577+
}
569578
});
570-
return pos;
579+
if (tried_to_erase_final_newline)
580+
{
581+
first = std::min(first, buffer.back_coord());
582+
pos = buffer.erase(buffer.back_coord(), buffer.end_coord());
583+
}
584+
return {first, pos};
571585
}
572586

573587
template<bool replace>
@@ -628,12 +642,12 @@ void pipe(Context& context, NormalParams params)
628642
if (in_lines.back().back() != '\n' and not out.empty() and out.back() == '\n')
629643
out.resize(out.length()-1, 0);
630644

631-
auto new_end = apply_diff(buffer, first, in_lines, out);
632-
if (new_end != first)
645+
auto [new_first, new_end] = apply_diff(buffer, first, in_lines, out);
646+
if (new_first != new_end)
633647
{
634648
auto& min = sel.min();
635649
auto& max = sel.max();
636-
min = first;
650+
min = new_first;
637651
max = buffer.char_prev(new_end);
638652
}
639653
else
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
%|printf '\n\n'<ret>
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
a
2+
b
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
2+

0 commit comments

Comments
 (0)