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:
parent
5df8073c3c
commit
47ba36c66e
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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"},
|
||||||
|
|
12
src/main.cc
12
src/main.cc
|
@ -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())
|
||||||
|
|
|
@ -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", '"' },
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue
Block a user