<commit_before>#include "normal.hh"

#include "buffer.hh"
#include "buffer_manager.hh"
#include "client_manager.hh"
#include "color_registry.hh"
#include "command_manager.hh"
#include "commands.hh"
#include "context.hh"
#include "file.hh"
#include "option_manager.hh"
#include "register_manager.hh"
#include "selectors.hh"
#include "shell_manager.hh"
#include "string.hh"
#include "window.hh"
#include "user_interface.hh"
#include "utf8_iterator.hh"
#include "debug.hh"

namespace Kakoune
{

void erase(Buffer& buffer, SelectionList& selections)
{
    for (auto& sel : reversed(selections))
    {
        erase(buffer, sel);
    }
    selections.update();
    selections.avoid_eol();
    buffer.check_invariant();
}

template<InsertMode mode>
BufferIterator prepare_insert(Buffer& buffer, const Selection& sel)
{
    switch (mode)
    {
    case InsertMode::Insert:
        return buffer.iterator_at(sel.min());
    case InsertMode::Replace:
        return Kakoune::erase(buffer, sel);
    case InsertMode::Append:
    {
        // special case for end of lines, append to current line instead
        auto pos = buffer.iterator_at(sel.max());
        return *pos == '\n' ? pos : utf8::next(pos);
    }
    case InsertMode::InsertAtLineBegin:
        return buffer.iterator_at(sel.min().line);
    case InsertMode::AppendAtLineEnd:
        return buffer.iterator_at({sel.max().line, buffer[sel.max().line].length() - 1});
    case InsertMode::InsertAtNextLineBegin:
        return buffer.iterator_at(sel.max().line+1);
    case InsertMode::OpenLineBelow:
        return buffer.insert(buffer.iterator_at(sel.max().line + 1), "\n");
    case InsertMode::OpenLineAbove:
        return buffer.insert(buffer.iterator_at(sel.min().line), "\n");
    }
    kak_assert(false);
    return {};
}

template<InsertMode mode>
void insert(Buffer& buffer, SelectionList& selections, memoryview<String> strings)
{
    if (strings.empty())
        return;
    for (size_t i = 0; i < selections.size(); ++i)
    {
        size_t index = selections.size() - 1 - i;
        auto& sel = selections[index];
        auto pos = prepare_insert<mode>(buffer, sel);
        const String& str = strings[std::min(index, strings.size()-1)];
        pos = buffer.insert(pos, str);
        if (mode == InsertMode::Replace)
        {
             if (pos == buffer.end())
                 --pos;
            selections.update();
            sel.anchor() = pos.coord();
            sel.cursor() = (str.empty() ?
                pos : pos + str.byte_count_to(str.char_length() - 1)).coord();
        }
    }
    selections.update();
    selections.avoid_eol();
    selections.check_invariant();
    buffer.check_invariant();
}

using namespace std::placeholders;
<commit_msg>Fix selection update in when doing a 'replace' insert<commit_after>#include "normal.hh"

#include "buffer.hh"
#include "buffer_manager.hh"
#include "client_manager.hh"
#include "color_registry.hh"
#include "command_manager.hh"
#include "commands.hh"
#include "context.hh"
#include "file.hh"
#include "option_manager.hh"
#include "register_manager.hh"
#include "selectors.hh"
#include "shell_manager.hh"
#include "string.hh"
#include "window.hh"
#include "user_interface.hh"
#include "utf8_iterator.hh"
#include "debug.hh"
#include "modification.hh"

namespace Kakoune
{

void erase(Buffer& buffer, SelectionList& selections)
{
    for (auto& sel : reversed(selections))
    {
        erase(buffer, sel);
    }
    selections.update();
    selections.avoid_eol();
    buffer.check_invariant();
}

template<InsertMode mode>
BufferIterator prepare_insert(Buffer& buffer, const Selection& sel)
{
    switch (mode)
    {
    case InsertMode::Insert:
        return buffer.iterator_at(sel.min());
    case InsertMode::Replace:
        return Kakoune::erase(buffer, sel);
    case InsertMode::Append:
    {
        // special case for end of lines, append to current line instead
        auto pos = buffer.iterator_at(sel.max());
        return *pos == '\n' ? pos : utf8::next(pos);
    }
    case InsertMode::InsertAtLineBegin:
        return buffer.iterator_at(sel.min().line);
    case InsertMode::AppendAtLineEnd:
        return buffer.iterator_at({sel.max().line, buffer[sel.max().line].length() - 1});
    case InsertMode::InsertAtNextLineBegin:
        return buffer.iterator_at(sel.max().line+1);
    case InsertMode::OpenLineBelow:
        return buffer.insert(buffer.iterator_at(sel.max().line + 1), "\n");
    case InsertMode::OpenLineAbove:
        return buffer.insert(buffer.iterator_at(sel.min().line), "\n");
    }
    kak_assert(false);
    return {};
}

template<InsertMode mode>
void insert(Buffer& buffer, SelectionList& selections, memoryview<String> strings)
{
    if (strings.empty())
        return;

    selections.update();
    for (size_t i = 0; i < selections.size(); ++i)
    {
        size_t index = selections.size() - 1 - i;
        auto& sel = selections[index];
        auto pos = prepare_insert<mode>(buffer, sel);
        const String& str = strings[std::min(index, strings.size()-1)];
        pos = buffer.insert(pos, str);
        if (mode == InsertMode::Replace)
        {
             if (pos == buffer.end())
                 --pos;
            sel.anchor() = pos.coord();
            sel.cursor() = (str.empty() ?
                pos : pos + str.byte_count_to(str.char_length() - 1)).coord();

           // update following selections
           auto changes = compute_modifications(buffer, selections.timestamp());
           for (size_t j = index+1; j < selections.size(); ++j)
           {
               auto& sel = selections[j];
               sel.anchor() = update_pos(changes, sel.anchor());
               sel.cursor() = update_pos(changes, sel.cursor());
           }
           selections.update_timestamp();
        }
    }
    selections.update();
    selections.avoid_eol();
    selections.check_invariant();
    buffer.check_invariant();
}

using namespace std::placeholders;

{'fraction_non_alphanumeric': 0.14431780087160576, 'fraction_numerical': 0.0031847133757961785, 'mean_word_length': 3.018181818181818, 'pattern_counts': {'":': 0, '<': 14, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6907899', 'n_tokens_mistral': 1867, 'n_tokens_neox': 1820, 'n_words': 396}