<commit_before>#include "client.hh"

#include "context.hh"
#include "register_manager.hh"

#include <unordered_map>

namespace Kakoune
{

extern std::unordered_map<Key, std::function<void (Context& context)>> keymap;

class Client::NormalMode : public Client::Mode
{
public:
    NormalMode(Client& client)
        : Client::Mode(client)
    {
    }

    void on_key(const Key& key, Context& context) override
    {
        if (key.modifiers == Key::Modifiers::None and isdigit(key.key))
            m_count = m_count * 10 + key.key - '0';
        else
        {
            auto it = keymap.find(key);
            if (it != keymap.end())
            {
                context.numeric_param(m_count);
                // it's important to do that before calling the command,
                // as we may die during the command execution.
                m_count = 0;
                it->second(context);
            }
            else
                m_count = 0;
        }
    }

private:
    int m_count = 0;
};


class Client::MenuMode : public Client::Mode
{
public:
    MenuMode(Client& client, const memoryview<String>& choices, MenuCallback callback)
        : Client::Mode(client),
          m_callback(callback), m_choice_count(choices.size()), m_selected(0)
    {
        client.show_menu(choices);
    }

    ~MenuMode()
    {
        m_client.menu_ctrl(MenuCommand::Close);
    }

    void on_key(const Key& key, Context& context) override
    {
        if (key == Key(Key::Modifiers::Control, 'n') or
            key == Key(Key::Modifiers::Control, 'i') or
            key == Key(Key::Modifiers::None, 'j'))
        {
            if (++m_selected >= m_choice_count)
            {
                m_client.menu_ctrl(MenuCommand::SelectFirst);
                m_selected = 0;
            }
            else
                m_client.menu_ctrl(MenuCommand::SelectNext);
        }
        if (key == Key(Key::Modifiers::Control, 'p') or
            key == Key(Key::Modifiers::None, 'k'))
        {
            if (--m_selected < 0)
            {
                m_client.menu_ctrl(MenuCommand::SelectLast);
                m_selected = m_choice_count-1;
            }
            else
                m_client.menu_ctrl(MenuCommand::SelectPrev);
        }
        if (key == Key(Key::Modifiers::Control, 'm'))
        {
            // save callback as reset_normal_mode will delete this
            MenuCallback callback = std::move(m_callback);
            int selected = m_selected;
            m_client.reset_normal_mode();
            callback(selected, context);
        }
        if (key == Key(Key::Modifiers::None, 27))
        {
            m_client.reset_normal_mode();
        }
        if (key.modifiers == Key::Modifiers::None and
            key.key >= '0' and key.key <= '9')
        {
            m_client.menu_ctrl(MenuCommand::Close);
            // save callback as reset_normal_mode will delete this
            MenuCallback callback = std::move(m_callback);
            m_client.reset_normal_mode();
            callback(key.key - '0' - 1, context);
        }
   }

private:
    MenuCallback m_callback;
    int          m_selected;
    int          m_choice_count;
};

class Client::PromptMode : public Client::Mode
{
public:
    PromptMode(Client& client, const String& prompt, Completer completer, PromptCallback callback)
        : Client::Mode(client),
          m_prompt(prompt), m_completer(completer), m_callback(callback), m_cursor_pos(0)
    {
        m_history_it = ms_history[m_prompt].end();
        m_client.print_status(m_prompt, m_prompt.length());
    }

    ~PromptMode()
    {
        m_client.menu_ctrl(MenuCommand::Close);
    }

    void on_key(const Key& key, Context& context) override
    {
        std::vector<String>& history = ms_history[m_prompt];
        if (key == Key(Key::Modifiers::Control, 'm')) // enter
        {
            std::vector<String>::iterator it;
            while ((it = find(history, m_result)) != history.end())
                history.erase(it);

            history.push_back(m_result);
            m_client.print_status("");
            // save callback as reset_normal_mode will delete this
            PromptCallback callback = std::move(m_callback);
            String result = std::move(m_result);
            m_client.reset_normal_mode();
            callback(result, context);
            return;
        }
        else if (key == Key(Key::Modifiers::None, 27))
        {
            m_client.print_status("");
            m_client.reset_normal_mode();
            return;
        }
        else if (key == Key(Key::Modifiers::Control, 'p'))
        {
            if (m_history_it != history.begin())
            {
                if (m_history_it == history.end())
                   m_saved_result = m_result;
                --m_history_it;
                m_result = *m_history_it;
                m_cursor_pos = m_result.length();
            }
        }
        else if (key == Key(Key::Modifiers::Control, 'n'))
        {
            if (m_history_it != history.end())
            {
                ++m_history_it;
                if (m_history_it != history.end())
                    m_result = *m_history_it;
                else
                    m_result = m_saved_result;
                m_cursor_pos = m_result.length();
            }
        }
        else if (key == Key(Key::Modifiers::Control, 'b'))
        {
            if (m_cursor_pos > 0)
                --m_cursor_pos;
        }
        else if (key == Key(Key::Modifiers::Control, 'f'))
        {
            if (m_cursor_pos < m_result.length())
                ++m_cursor_pos;
        }
        else if (key == Key(Key::Modifiers::Control, 'g')) // backspace
        {
            if (m_cursor_pos != 0)
            {
                m_result = m_result.substr(0, m_cursor_pos - 1)
                       + m_result.substr(m_cursor_pos, String::npos);

                --m_cursor_pos;
            }

            m_client.menu_ctrl(MenuCommand::Close);
            m_current_completion = -1;
        }
        else if (key == Key(Key::Modifiers::Control, 'r'))
        {
            Key k = m_client.get_key();
            String reg = RegisterManager::instance()[k.key].values(context)[0];
            m_client.menu_ctrl(MenuCommand::Close);
            m_current_completion = -1;
            m_result = m_result.substr(0, m_cursor_pos) + reg + m_result.substr(m_cursor_pos, String::npos);
            m_cursor_pos += reg.length();
        }
        else if (key == Key(Key::Modifiers::Control, 'i')) // tab
        {<commit_msg>Allow arrow keys in prompt mode<commit_after>#include "client.hh"

#include "context.hh"
#include "register_manager.hh"

#include <unordered_map>

namespace Kakoune
{

extern std::unordered_map<Key, std::function<void (Context& context)>> keymap;

class Client::NormalMode : public Client::Mode
{
public:
    NormalMode(Client& client)
        : Client::Mode(client)
    {
    }

    void on_key(const Key& key, Context& context) override
    {
        if (key.modifiers == Key::Modifiers::None and isdigit(key.key))
            m_count = m_count * 10 + key.key - '0';
        else
        {
            auto it = keymap.find(key);
            if (it != keymap.end())
            {
                context.numeric_param(m_count);
                // it's important to do that before calling the command,
                // as we may die during the command execution.
                m_count = 0;
                it->second(context);
            }
            else
                m_count = 0;
        }
    }

private:
    int m_count = 0;
};


class Client::MenuMode : public Client::Mode
{
public:
    MenuMode(Client& client, const memoryview<String>& choices, MenuCallback callback)
        : Client::Mode(client),
          m_callback(callback), m_choice_count(choices.size()), m_selected(0)
    {
        client.show_menu(choices);
    }

    ~MenuMode()
    {
        m_client.menu_ctrl(MenuCommand::Close);
    }

    void on_key(const Key& key, Context& context) override
    {
        if (key == Key(Key::Modifiers::Control, 'n') or
            key == Key(Key::Modifiers::Control, 'i') or
            key == Key(Key::Modifiers::None, 'j'))
        {
            if (++m_selected >= m_choice_count)
            {
                m_client.menu_ctrl(MenuCommand::SelectFirst);
                m_selected = 0;
            }
            else
                m_client.menu_ctrl(MenuCommand::SelectNext);
        }
        if (key == Key(Key::Modifiers::Control, 'p') or
            key == Key(Key::Modifiers::None, 'k'))
        {
            if (--m_selected < 0)
            {
                m_client.menu_ctrl(MenuCommand::SelectLast);
                m_selected = m_choice_count-1;
            }
            else
                m_client.menu_ctrl(MenuCommand::SelectPrev);
        }
        if (key == Key(Key::Modifiers::Control, 'm'))
        {
            // save callback as reset_normal_mode will delete this
            MenuCallback callback = std::move(m_callback);
            int selected = m_selected;
            m_client.reset_normal_mode();
            callback(selected, context);
        }
        if (key == Key(Key::Modifiers::None, 27))
        {
            m_client.reset_normal_mode();
        }
        if (key.modifiers == Key::Modifiers::None and
            key.key >= '0' and key.key <= '9')
        {
            m_client.menu_ctrl(MenuCommand::Close);
            // save callback as reset_normal_mode will delete this
            MenuCallback callback = std::move(m_callback);
            m_client.reset_normal_mode();
            callback(key.key - '0' - 1, context);
        }
   }

private:
    MenuCallback m_callback;
    int          m_selected;
    int          m_choice_count;
};

class Client::PromptMode : public Client::Mode
{
public:
    PromptMode(Client& client, const String& prompt, Completer completer, PromptCallback callback)
        : Client::Mode(client),
          m_prompt(prompt), m_completer(completer), m_callback(callback), m_cursor_pos(0)
    {
        m_history_it = ms_history[m_prompt].end();
        m_client.print_status(m_prompt, m_prompt.length());
    }

    ~PromptMode()
    {
        m_client.menu_ctrl(MenuCommand::Close);
    }

    void on_key(const Key& key, Context& context) override
    {
        std::vector<String>& history = ms_history[m_prompt];
        if (key == Key(Key::Modifiers::Control, 'm')) // enter
        {
            std::vector<String>::iterator it;
            while ((it = find(history, m_result)) != history.end())
                history.erase(it);

            history.push_back(m_result);
            m_client.print_status("");
            // save callback as reset_normal_mode will delete this
            PromptCallback callback = std::move(m_callback);
            String result = std::move(m_result);
            m_client.reset_normal_mode();
            callback(result, context);
            return;
        }
        else if (key == Key(Key::Modifiers::None, 27))
        {
            m_client.print_status("");
            m_client.reset_normal_mode();
            return;
        }
        else if (key == Key(Key::Modifiers::Control, 'p') or
                 key == Key(Key::Modifiers::Control, 'c'))
        {
            if (m_history_it != history.begin())
            {
                if (m_history_it == history.end())
                   m_saved_result = m_result;
                --m_history_it;
                m_result = *m_history_it;
                m_cursor_pos = m_result.length();
            }
        }
        else if (key == Key(Key::Modifiers::Control, 'n') or
                 key == Key(Key::Modifiers::Control, 'b'))
        {
            if (m_history_it != history.end())
            {
                ++m_history_it;
                if (m_history_it != history.end())
                    m_result = *m_history_it;
                else
                    m_result = m_saved_result;
                m_cursor_pos = m_result.length();
            }
        }
        else if (key == Key(Key::Modifiers::Control, 'd'))
        {
            if (m_cursor_pos > 0)
                --m_cursor_pos;
        }
        else if (key == Key(Key::Modifiers::Control, 'e'))
        {
            if (m_cursor_pos < m_result.length())
                ++m_cursor_pos;
        }
        else if (key == Key(Key::Modifiers::Control, 'g')) // backspace
        {
            if (m_cursor_pos != 0)
            {
                m_result = m_result.substr(0, m_cursor_pos - 1)
                       + m_result.substr(m_cursor_pos, String::npos);

                --m_cursor_pos;
            }

            m_client.menu_ctrl(MenuCommand::Close);
            m_current_completion = -1;
        }
        else if (key == Key(Key::Modifiers::Control, 'r'))
        {
            Key k = m_client.get_key();
            String reg = RegisterManager::instance()[k.key].values(context)[0];
            m_client.menu_ctrl(MenuCommand::Close);
            m_current_completion = -1;
            m_result = m_result.substr(0, m_cursor_pos) + reg + m_result.substr(m_cursor_pos, String::npos);
            m_cursor_pos += reg.length();
        }
        else if (key == Key(Key::Modifiers::Control, 'i')) // tab
        {
{'fraction_non_alphanumeric': 0.12419379031048447, 'fraction_numerical': 0.004049797510124494, 'mean_word_length': 1.7393179950698439, 'pattern_counts': {'":': 0, '<': 21, '<?xml version=': 0, '>': 23, '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': '3044216', 'n_tokens_mistral': 4020, 'n_tokens_neox': 3893, 'n_words': 757}