diff --git a/src/buffer.cc b/src/buffer.cc index 5d3702eb..40b07eb6 100644 --- a/src/buffer.cc +++ b/src/buffer.cc @@ -484,7 +484,7 @@ BufferCoord Buffer::do_insert(BufferCoord pos, StringView content) : BufferCoord{ last_line, m_lines[last_line].length() - suffix.length() }; m_changes.push_back({ Change::Insert, pos, end }); - return pos; + return end; } BufferCoord Buffer::do_erase(BufferCoord begin, BufferCoord end) @@ -809,7 +809,8 @@ UnitTest test_buffer{[]() UnitTest test_undo{[]() { Buffer buffer("test", Buffer::Flags::None, "allo ?\nmais que fais la police\n hein ?\n youpi\n"); - auto pos = buffer.insert(buffer.end_coord(), "kanaky\n"); // change 1 + auto pos = buffer.end_coord(); + buffer.insert(pos, "kanaky\n"); // change 1 buffer.commit_undo_group(); buffer.erase(pos, buffer.end_coord()); // change 2 buffer.commit_undo_group(); diff --git a/src/normal.cc b/src/normal.cc index c219977b..e8c5cdb7 100644 --- a/src/normal.cc +++ b/src/normal.cc @@ -489,7 +489,7 @@ void command(Context& context, NormalParams params) }); } -void apply_diff(Buffer& buffer, BufferCoord pos, StringView before, StringView after) +BufferCoord apply_diff(Buffer& buffer, BufferCoord pos, StringView before, StringView after) { // The diff algorithm is O(ND) with N the sum of string len, and D the diff count // do not use it if our data is too big @@ -497,8 +497,7 @@ void apply_diff(Buffer& buffer, BufferCoord pos, StringView before, StringView a if (before.length() + after.length() > size_limit) { buffer.erase(pos, buffer.advance(pos, before.length())); - buffer.insert(pos, after); - return; + return buffer.insert(pos, after); } auto diffs = find_diff(before.begin(), (int)before.length(), after.begin(), (int)after.length()); @@ -511,14 +510,14 @@ void apply_diff(Buffer& buffer, BufferCoord pos, StringView before, StringView a pos = buffer.advance(pos, diff.len); break; case Diff::Add: - buffer.insert(pos, after.substr(ByteCount{diff.posB}, ByteCount{diff.len})); - pos = buffer.advance(pos, diff.len); + pos = buffer.insert(pos, after.substr(ByteCount{diff.posB}, ByteCount{diff.len})); break; case Diff::Remove: - buffer.erase(pos, buffer.advance(pos, diff.len)); + pos = buffer.erase(pos, buffer.advance(pos, diff.len)); break; } } + return pos; } template @@ -544,19 +543,14 @@ void pipe(Context& context, NormalParams) Buffer& buffer = context.buffer(); SelectionList selections = context.selections(); - auto restore_sels = on_scope_end([&, old_main = selections.main_index()] { - selections.set_main_index(old_main); - context.selections() = std::move(selections); - }); if (replace) { ScopedEdition edition(context); ForwardChangesTracker changes_tracker; size_t timestamp = buffer.timestamp(); - for (int i = 0; i < selections.size(); ++i) + Vector new_sels; + for (auto& sel : selections) { - selections.set_main_index(i); - auto& sel = selections.main(); const auto beg = changes_tracker.get_new_coord_tolerant(sel.min()); const auto end = changes_tracker.get_new_coord_tolerant(sel.max()); @@ -566,7 +560,7 @@ void pipe(Context& context, NormalParams) in += '\n'; // Needed in case we read selections inside the cmdline - context.selections_write_only() = selections; + context.selections_write_only().set({keep_direction(Selection{beg, end}, sel)}, 0); String out = ShellManager::instance().eval( cmdline, context, in, @@ -578,13 +572,23 @@ void pipe(Context& context, NormalParams) if (not out.empty() and out.back() == '\n') out.resize(out.length()-1, 0); } - apply_diff(buffer, beg, in, out); + auto new_end = apply_diff(buffer, beg, in, out); + if (new_end != beg) + new_sels.push_back(keep_direction({beg, buffer.char_prev(new_end), std::move(sel.captures())}, sel)); + else + { + if (new_end != BufferCoord{}) + new_end = buffer.char_prev(new_end); + new_sels.push_back({new_end, new_end, std::move(sel.captures())}); + } changes_tracker.update(buffer, timestamp); } + context.selections_write_only().set(std::move(new_sels), selections.main_index()); } else { + const auto old_main = selections.main_index(); for (int i = 0; i < selections.size(); ++i) { selections.set_main_index(i); @@ -592,6 +596,7 @@ void pipe(Context& context, NormalParams) content(buffer, selections.main()), ShellManager::Flags::None); } + selections.set_main_index(old_main); } }); } diff --git a/src/selection.cc b/src/selection.cc index c245095d..5e3ff2e7 100644 --- a/src/selection.cc +++ b/src/selection.cc @@ -409,8 +409,12 @@ void SelectionList::insert(ConstArrayView strings, InsertMode mode, const String& str = strings[std::min(index, strings.size()-1)]; const auto pos = (mode == InsertMode::Replace) ? - replace(*m_buffer, sel, str) - : m_buffer->insert(changes_tracker.get_new_coord(insert_pos[index]), str); + sel.min() : changes_tracker.get_new_coord(insert_pos[index]); + + if (mode == InsertMode::Replace) + replace(*m_buffer, sel, str); + else + m_buffer->insert(pos, str); size_t old_timestamp = m_timestamp; changes_tracker.update(*m_buffer, m_timestamp); diff --git a/test/compose/complex-pipe/cmd b/test/compose/complex-pipe/cmd new file mode 100644 index 00000000..2636360b --- /dev/null +++ b/test/compose/complex-pipe/cmd @@ -0,0 +1 @@ +|sort diff --git a/test/compose/complex-pipe/in b/test/compose/complex-pipe/in new file mode 100644 index 00000000..330f63e5 --- /dev/null +++ b/test/compose/complex-pipe/in @@ -0,0 +1,8 @@ +foo %(baz +bar +qux) quux + +foo %(baz +bar +qux +) diff --git a/test/compose/complex-pipe/out b/test/compose/complex-pipe/out new file mode 100644 index 00000000..50472e73 --- /dev/null +++ b/test/compose/complex-pipe/out @@ -0,0 +1,8 @@ +foo bar +baz +qux quux + +foo bar +baz +qux + diff --git a/test/compose/complex-pipe/selections b/test/compose/complex-pipe/selections new file mode 100644 index 00000000..1873d212 --- /dev/null +++ b/test/compose/complex-pipe/selections @@ -0,0 +1,6 @@ +'bar +baz +qux' 'bar +baz +qux +' diff --git a/test/regression/0-crash-on-pipe-with-selection-access/selections b/test/regression/0-crash-on-pipe-with-selection-access/selections new file mode 100644 index 00000000..aeceb627 --- /dev/null +++ b/test/regression/0-crash-on-pipe-with-selection-access/selections @@ -0,0 +1,2 @@ +'yes +' diff --git a/test/regression/1504-assertion-on-incorrect-pipe-use/state b/test/regression/1504-assertion-on-incorrect-pipe-use/state new file mode 100644 index 00000000..76bd5e3b --- /dev/null +++ b/test/regression/1504-assertion-on-incorrect-pipe-use/state @@ -0,0 +1 @@ +1.1,1.1