Add a RegisterModified hook

This one has been a long time coming, I am still concerned this could
impact performance a lot. This hook does *not* trigger for capture
registers (0-9) or any other dynamic registers (that are not writable).

Fixes #859
This commit is contained in:
Maxime Coste 2020-07-19 12:56:55 +10:00
parent 5df8073c3c
commit 47ba36c66e
5 changed files with 74 additions and 49 deletions

View File

@ -175,6 +175,9 @@ name. Hooks with no description will always use an empty string.
even with the `-with-hooks` option (see even with the `-with-hooks` option (see
<<execeval#execute-keys-specific-switches,`:doc execeval execute-keys-specific-switches`>>). <<execeval#execute-keys-specific-switches,`:doc execeval execute-keys-specific-switches`>>).
*RegisterModified* `register`::
Triggered after a register has been written to.
*ModuleLoaded* `module`:: *ModuleLoaded* `module`::
Triggered after a module is evaluated by the first `require-module` call Triggered after a module is evaluated by the first `require-module` call

View File

@ -49,6 +49,7 @@ enum class Hook
NormalKey, NormalKey,
ModeChange, ModeChange,
RawKey, RawKey,
RegisterModified,
WinClose, WinClose,
WinCreate, WinCreate,
WinDisplay, WinDisplay,
@ -93,6 +94,7 @@ constexpr auto enum_desc(Meta::Type<Hook>)
{Hook::NormalKey, "NormalKey"}, {Hook::NormalKey, "NormalKey"},
{Hook::ModeChange, "ModeChange"}, {Hook::ModeChange, "ModeChange"},
{Hook::RawKey, "RawKey"}, {Hook::RawKey, "RawKey"},
{Hook::RegisterModified, "RegisterModified"},
{Hook::WinClose, "WinClose"}, {Hook::WinClose, "WinClose"},
{Hook::WinCreate, "WinCreate"}, {Hook::WinCreate, "WinCreate"},
{Hook::WinDisplay, "WinDisplay"}, {Hook::WinDisplay, "WinDisplay"},

View File

@ -350,25 +350,28 @@ void register_registers()
{ {
RegisterManager& register_manager = RegisterManager::instance(); RegisterManager& register_manager = RegisterManager::instance();
for (auto c : StringView{"abcdefghijklmnopqrstuvwxyz\"^@"}) for (Codepoint c : StringView{"abcdefghijklmnopqrstuvwxyz\"^@"})
register_manager.add_register(c, std::make_unique<StaticRegister>()); register_manager.add_register(c, std::make_unique<StaticRegister>(String{c}));
for (auto c : StringView{"/|:\\"}) for (Codepoint c : StringView{"/|:\\"})
register_manager.add_register(c, std::make_unique<HistoryRegister>()); register_manager.add_register(c, std::make_unique<HistoryRegister>(String{c}));
using StringList = Vector<String, MemoryDomain::Registers>; using StringList = Vector<String, MemoryDomain::Registers>;
register_manager.add_register('%', make_dyn_reg( register_manager.add_register('%', make_dyn_reg(
"%",
[](const Context& context) [](const Context& context)
{ return StringList{{context.buffer().display_name()}}; })); { return StringList{{context.buffer().display_name()}}; }));
register_manager.add_register('.', make_dyn_reg( register_manager.add_register('.', make_dyn_reg(
".",
[](const Context& context) { [](const Context& context) {
auto content = context.selections_content(); auto content = context.selections_content();
return StringList{content.begin(), content.end()}; return StringList{content.begin(), content.end()};
})); }));
register_manager.add_register('#', make_dyn_reg( register_manager.add_register('#', make_dyn_reg(
"#",
[](const Context& context) { [](const Context& context) {
const size_t count = context.selections().size(); const size_t count = context.selections().size();
StringList res; StringList res;
@ -381,6 +384,7 @@ void register_registers()
for (size_t i = 0; i < 10; ++i) for (size_t i = 0; i < 10; ++i)
{ {
register_manager.add_register('0'+i, make_dyn_reg( register_manager.add_register('0'+i, make_dyn_reg(
String{Codepoint('0'+i)},
[i](const Context& context) { [i](const Context& context) {
StringList result; StringList result;
for (auto& sel : context.selections()) for (auto& sel : context.selections())

View File

@ -1,12 +1,59 @@
#include "register_manager.hh" #include "register_manager.hh"
#include "assert.hh" #include "assert.hh"
#include "context.hh"
#include "hash_map.hh" #include "hash_map.hh"
#include "string_utils.hh" #include "string_utils.hh"
namespace Kakoune namespace Kakoune
{ {
void StaticRegister::set(Context& context, ConstArrayView<String> values, bool)
{
m_content.assign(values.begin(), values.end());
context.hooks().run_hook(Hook::RegisterModified, m_name, context);
}
ConstArrayView<String> StaticRegister::get(const Context&)
{
if (m_content.empty())
return ConstArrayView<String>(String::ms_empty);
else
return ConstArrayView<String>(m_content);
}
const String& StaticRegister::get_main(const Context& context, size_t main_index)
{
auto content = get(context);
return content[std::min(main_index, content.size() - 1)];
}
void HistoryRegister::set(Context& context, ConstArrayView<String> values, bool restoring)
{
constexpr size_t size_limit = 100;
if (restoring)
return StaticRegister::set(context, values, true);
for (auto& entry : values)
{
m_content.erase(std::remove(m_content.begin(), m_content.end(), entry),
m_content.end());
m_content.push_back(entry);
}
const size_t current_size = m_content.size();
if (current_size > size_limit)
m_content.erase(m_content.begin(), m_content.begin() + (current_size - size_limit));
context.hooks().run_hook(Hook::RegisterModified, m_name, context);
}
const String& HistoryRegister::get_main(const Context&, size_t)
{
return m_content.empty() ? String::ms_empty : m_content.back();
}
static const HashMap<String, Codepoint> reg_names = { static const HashMap<String, Codepoint> reg_names = {
{ "slash", '/' }, { "slash", '/' },
{ "dquote", '"' }, { "dquote", '"' },

View File

@ -33,26 +33,14 @@ public:
class StaticRegister : public Register class StaticRegister : public Register
{ {
public: public:
void set(Context&, ConstArrayView<String> values, bool) override StaticRegister(String name) : m_name{std::move(name)} {}
{
m_content.assign(values.begin(), values.end());
}
ConstArrayView<String> get(const Context&) override void set(Context& context, ConstArrayView<String> values, bool) override;
{ ConstArrayView<String> get(const Context&) override;
if (m_content.empty()) const String& get_main(const Context& context, size_t main_index) override;
return ConstArrayView<String>(String::ms_empty);
else
return ConstArrayView<String>(m_content);
}
const String& get_main(const Context& context, size_t main_index) override
{
auto content = get(context);
return content[std::min(main_index, content.size() - 1)];
}
protected: protected:
String m_name;
Vector<String, MemoryDomain::Registers> m_content; Vector<String, MemoryDomain::Registers> m_content;
}; };
@ -62,8 +50,8 @@ template<typename Getter, typename Setter>
class DynamicRegister : public StaticRegister class DynamicRegister : public StaticRegister
{ {
public: public:
DynamicRegister(Getter getter, Setter setter) DynamicRegister(String name, Getter getter, Setter setter)
: m_getter(std::move(getter)), m_setter(std::move(setter)) {} : StaticRegister(std::move(name)), m_getter(std::move(getter)), m_setter(std::move(setter)) {}
void set(Context& context, ConstArrayView<String> values, bool) override void set(Context& context, ConstArrayView<String> values, bool) override
{ {
@ -85,45 +73,26 @@ private:
class HistoryRegister : public StaticRegister class HistoryRegister : public StaticRegister
{ {
public: public:
void set(Context& context, ConstArrayView<String> values, bool restoring) override using StaticRegister::StaticRegister;
{
constexpr size_t size_limit = 100;
if (restoring) void set(Context& context, ConstArrayView<String> values, bool restoring) override;
return StaticRegister::set(context, values, true); const String& get_main(const Context&, size_t) override;
for (auto& entry : values)
{
m_content.erase(std::remove(m_content.begin(), m_content.end(), entry),
m_content.end());
m_content.push_back(entry);
}
const size_t current_size = m_content.size();
if (current_size > size_limit)
m_content.erase(m_content.begin(), m_content.begin() + (current_size - size_limit));
}
const String& get_main(const Context&, size_t) override
{
return m_content.empty() ? String::ms_empty : m_content.back();
}
}; };
template<typename Func> template<typename Func>
std::unique_ptr<Register> make_dyn_reg(Func func) std::unique_ptr<Register> make_dyn_reg(String name, Func func)
{ {
auto setter = [](Context&, ConstArrayView<String>) auto setter = [](Context&, ConstArrayView<String>)
{ {
throw runtime_error("this register is not assignable"); throw runtime_error("this register is not assignable");
}; };
return std::make_unique<DynamicRegister<Func, decltype(setter)>>(std::move(func), setter); return std::make_unique<DynamicRegister<Func, decltype(setter)>>(name, std::move(func), setter);
} }
template<typename Getter, typename Setter> template<typename Getter, typename Setter>
std::unique_ptr<Register> make_dyn_reg(Getter getter, Setter setter) std::unique_ptr<Register> make_dyn_reg(String name, Getter getter, Setter setter)
{ {
return std::make_unique<DynamicRegister<Getter, Setter>>(std::move(getter), std::move(setter)); return std::make_unique<DynamicRegister<Getter, Setter>>(name, std::move(getter), std::move(setter));
} }
class NullRegister : public Register class NullRegister : public Register