Use register to store prompt history
This commit is contained in:
parent
a9e778fcc7
commit
e613292568
|
@ -1739,10 +1739,10 @@ void context_wrap(const ParametersParser& parser, Context& context, StringView d
|
|||
|
||||
auto& register_manager = RegisterManager::instance();
|
||||
auto make_register_restorer = [&](char c) {
|
||||
return on_scope_end([&, c, save=register_manager[c].get(context) | gather<Vector<String>>()] {
|
||||
return on_scope_end([&, c, save=register_manager[c].save(context)] {
|
||||
try
|
||||
{
|
||||
RegisterManager::instance()[c].set(context, save);
|
||||
RegisterManager::instance()[c].restore(context, save);
|
||||
}
|
||||
catch (runtime_error& err)
|
||||
{
|
||||
|
@ -2001,7 +2001,7 @@ const CommandDesc prompt_cmd = {
|
|||
CapturedShellContext sc{shell_context};
|
||||
context.input_handler().prompt(
|
||||
parser[0], initstr.str(), {}, context.faces()["Prompt"],
|
||||
flags, std::move(completer),
|
||||
flags, '_', std::move(completer),
|
||||
[=](StringView str, PromptEvent event, Context& context) mutable
|
||||
{
|
||||
if ((event == PromptEvent::Abort and on_abort.empty()) or
|
||||
|
|
|
@ -239,11 +239,8 @@ void Context::end_edition()
|
|||
|
||||
StringView Context::main_sel_register_value(StringView reg) const
|
||||
{
|
||||
auto strings = RegisterManager::instance()[reg].get(*this);
|
||||
size_t index = m_selections ? (*m_selections).main_index() : 0;
|
||||
if (strings.size() <= index)
|
||||
index = strings.size() - 1;
|
||||
return strings[index];
|
||||
return RegisterManager::instance()[reg].get_main(*this, index);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -751,11 +751,13 @@ class Prompt : public InputMode
|
|||
public:
|
||||
Prompt(InputHandler& input_handler, StringView prompt,
|
||||
String initstr, String emptystr, Face face, PromptFlags flags,
|
||||
PromptCompleter completer, PromptCallback callback)
|
||||
char history_register, PromptCompleter completer, PromptCallback callback)
|
||||
: InputMode(input_handler), m_callback(std::move(callback)), m_completer(std::move(completer)),
|
||||
m_prompt(prompt.str()), m_prompt_face(face),
|
||||
m_empty_text{std::move(emptystr)},
|
||||
m_line_editor{context().faces()}, m_flags(flags),
|
||||
m_history{RegisterManager::instance()[history_register]},
|
||||
m_current_history{m_history.get(context()).size()},
|
||||
m_auto_complete{context().options()["autocomplete"].get<AutoComplete>() & AutoComplete::Prompt},
|
||||
m_idle_timer{TimePoint::max(), context().flags() & Context::Flags::Draft ?
|
||||
Timer::Callback{} : [this](Timer&) {
|
||||
|
@ -769,13 +771,11 @@ public:
|
|||
context().hooks().run_hook(Hook::PromptIdle, "", context());
|
||||
}}
|
||||
{
|
||||
m_history_it = ms_history[m_prompt].end();
|
||||
m_line_editor.reset(std::move(initstr), m_empty_text);
|
||||
}
|
||||
|
||||
void on_key(Key key) override
|
||||
{
|
||||
History& history = ms_history[m_prompt];
|
||||
const String& line = m_line_editor.line();
|
||||
|
||||
if (key == Key::Return)
|
||||
|
@ -790,7 +790,7 @@ public:
|
|||
}
|
||||
|
||||
if (not context().history_disabled())
|
||||
history_push(history, line);
|
||||
history_push(line);
|
||||
context().print_status(DisplayLine{});
|
||||
if (context().has_client())
|
||||
context().client().menu_hide();
|
||||
|
@ -807,7 +807,7 @@ public:
|
|||
else if (key == Key::Escape or key == ctrl('c'))
|
||||
{
|
||||
if (not context().history_disabled())
|
||||
history_push(history, line);
|
||||
history_push(line);
|
||||
context().print_status(DisplayLine{});
|
||||
if (context().has_client())
|
||||
context().client().menu_hide();
|
||||
|
@ -853,22 +853,25 @@ public:
|
|||
}
|
||||
else if (key == Key::Up or key == ctrl('p'))
|
||||
{
|
||||
if (m_history_it != history.begin())
|
||||
if (m_current_history != 0)
|
||||
{
|
||||
if (m_history_it == history.end())
|
||||
auto history = m_history.get(context());
|
||||
// The history register might have been mutated in the mean time
|
||||
m_current_history = std::min(history.size(), m_current_history);
|
||||
if (m_current_history == history.size())
|
||||
m_prefix = line;
|
||||
auto it = m_history_it;
|
||||
auto index = m_current_history;
|
||||
// search for the previous history entry matching typed prefix
|
||||
do
|
||||
{
|
||||
--it;
|
||||
if (prefix_match(*it, m_prefix))
|
||||
--index;
|
||||
if (prefix_match(history[index], m_prefix))
|
||||
{
|
||||
m_history_it = it;
|
||||
m_line_editor.reset(*it, m_empty_text);
|
||||
m_current_history = index;
|
||||
m_line_editor.reset(history[index], m_empty_text);
|
||||
break;
|
||||
}
|
||||
} while (it != history.begin());
|
||||
} while (index != 0);
|
||||
|
||||
clear_completions();
|
||||
m_refresh_completion_pending = true;
|
||||
|
@ -876,16 +879,19 @@ public:
|
|||
}
|
||||
else if (key == Key::Down or key == ctrl('n')) // next
|
||||
{
|
||||
if (m_history_it != history.end())
|
||||
auto history = m_history.get(context());
|
||||
// The history register might have been mutated in the mean time
|
||||
m_current_history = std::min(history.size(), m_current_history);
|
||||
if (m_current_history < history.size())
|
||||
{
|
||||
// search for the next history entry matching typed prefix
|
||||
++m_history_it;
|
||||
while (m_history_it != history.end() and
|
||||
not prefix_match(*m_history_it, m_prefix))
|
||||
++m_history_it;
|
||||
++m_current_history;
|
||||
while (m_current_history != history.size() and
|
||||
not prefix_match(history[m_current_history], m_prefix))
|
||||
++m_current_history;
|
||||
|
||||
if (m_history_it != history.end())
|
||||
m_line_editor.reset(*m_history_it, m_empty_text);
|
||||
if (m_current_history != history.size())
|
||||
m_line_editor.reset(history[m_current_history], m_empty_text);
|
||||
else
|
||||
m_line_editor.reset(m_prefix, m_empty_text);
|
||||
|
||||
|
@ -1089,27 +1095,22 @@ private:
|
|||
LineEditor m_line_editor;
|
||||
bool m_line_changed = false;
|
||||
PromptFlags m_flags;
|
||||
Register& m_history;
|
||||
size_t m_current_history;
|
||||
bool m_auto_complete;
|
||||
bool m_refresh_completion_pending = true;
|
||||
Timer m_idle_timer;
|
||||
|
||||
using History = Vector<String, MemoryDomain::History>;
|
||||
static HashMap<String, History, MemoryDomain::History> ms_history;
|
||||
History::iterator m_history_it;
|
||||
|
||||
void history_push(History& history, StringView entry)
|
||||
void history_push(StringView entry)
|
||||
{
|
||||
if (entry.empty() or
|
||||
(m_flags & PromptFlags::DropHistoryEntriesWithBlankPrefix and
|
||||
is_horizontal_blank(entry[0_byte])))
|
||||
return;
|
||||
|
||||
history.erase(std::remove(history.begin(), history.end(), entry),
|
||||
history.end());
|
||||
history.push_back(entry.str());
|
||||
m_history.set(context(), {entry.str()});
|
||||
}
|
||||
};
|
||||
HashMap<String, Prompt::History, MemoryDomain::History> Prompt::ms_history;
|
||||
|
||||
class NextKey : public InputMode
|
||||
{
|
||||
|
@ -1592,11 +1593,12 @@ void InputHandler::repeat_last_insert()
|
|||
}
|
||||
|
||||
void InputHandler::prompt(StringView prompt, String initstr, String emptystr,
|
||||
Face prompt_face, PromptFlags flags,
|
||||
Face prompt_face, PromptFlags flags, char history_register,
|
||||
PromptCompleter completer, PromptCallback callback)
|
||||
{
|
||||
push_mode(new InputModes::Prompt(*this, prompt, std::move(initstr), std::move(emptystr),
|
||||
prompt_face, flags, std::move(completer), std::move(callback)));
|
||||
prompt_face, flags, history_register,
|
||||
std::move(completer), std::move(callback)));
|
||||
}
|
||||
|
||||
void InputHandler::set_prompt_face(Face prompt_face)
|
||||
|
|
|
@ -68,7 +68,7 @@ public:
|
|||
// returns to normal mode after validation if callback does
|
||||
// not change the mode itself
|
||||
void prompt(StringView prompt, String initstr, String emptystr,
|
||||
Face prompt_face, PromptFlags flags,
|
||||
Face prompt_face, PromptFlags flags, char history_register,
|
||||
PromptCompleter completer, PromptCallback callback);
|
||||
void set_prompt_face(Face prompt_face);
|
||||
|
||||
|
|
|
@ -280,9 +280,12 @@ void register_registers()
|
|||
{
|
||||
RegisterManager& register_manager = RegisterManager::instance();
|
||||
|
||||
for (auto c : "abcdefghijklmnopqrstuvwxyz/\"|^@:")
|
||||
for (auto c : StringView{"abcdefghijklmnopqrstuvwxyz\"^@"})
|
||||
register_manager.add_register(c, std::make_unique<StaticRegister>());
|
||||
|
||||
for (auto c : StringView{"/|:\\"})
|
||||
register_manager.add_register(c, std::make_unique<HistoryRegister>());
|
||||
|
||||
using StringList = Vector<String, MemoryDomain::Registers>;
|
||||
|
||||
register_manager.add_register('%', make_dyn_reg(
|
||||
|
|
|
@ -456,6 +456,7 @@ void command(Context& context, EnvVarMap env_vars)
|
|||
context.input_handler().prompt(
|
||||
":", {}, context.main_sel_register_value(':').str(),
|
||||
context.faces()["Prompt"], PromptFlags::DropHistoryEntriesWithBlankPrefix,
|
||||
':',
|
||||
[](const Context& context, CompletionFlags flags,
|
||||
StringView cmd_line, ByteCount pos) {
|
||||
return CommandManager::instance().complete(context, flags, cmd_line, pos);
|
||||
|
@ -544,7 +545,7 @@ void pipe(Context& context, NormalParams)
|
|||
const char* prompt = replace ? "pipe:" : "pipe-to:";
|
||||
context.input_handler().prompt(
|
||||
prompt, {}, context.main_sel_register_value("|").str(), context.faces()["Prompt"],
|
||||
PromptFlags::DropHistoryEntriesWithBlankPrefix,
|
||||
PromptFlags::DropHistoryEntriesWithBlankPrefix, '|',
|
||||
shell_complete,
|
||||
[](StringView cmdline, PromptEvent event, Context& context)
|
||||
{
|
||||
|
@ -625,7 +626,7 @@ void insert_output(Context& context, NormalParams)
|
|||
const char* prompt = mode == InsertMode::Insert ? "insert-output:" : "append-output:";
|
||||
context.input_handler().prompt(
|
||||
prompt, {}, context.main_sel_register_value("|").str(), context.faces()["Prompt"],
|
||||
PromptFlags::DropHistoryEntriesWithBlankPrefix,
|
||||
PromptFlags::DropHistoryEntriesWithBlankPrefix, '|',
|
||||
shell_complete,
|
||||
[](StringView cmdline, PromptEvent event, Context& context)
|
||||
{
|
||||
|
@ -753,14 +754,15 @@ constexpr RegexCompileFlags direction_flags(RegexMode mode)
|
|||
}
|
||||
|
||||
template<RegexMode mode = RegexMode::Forward, typename T>
|
||||
void regex_prompt(Context& context, String prompt, String default_regex, T func)
|
||||
void regex_prompt(Context& context, String prompt, char reg, T func)
|
||||
{
|
||||
static_assert(is_direction(mode));
|
||||
DisplayCoord position = context.has_window() ? context.window().position() : DisplayCoord{};
|
||||
SelectionList selections = context.selections();
|
||||
auto default_regex = RegisterManager::instance()[reg].get_main(context, context.selections().main_index());
|
||||
context.input_handler().prompt(
|
||||
std::move(prompt), {}, default_regex, context.faces()["Prompt"],
|
||||
PromptFlags::Search,
|
||||
PromptFlags::Search, reg,
|
||||
[](const Context& context, CompletionFlags, StringView regex, ByteCount pos) -> Completions {
|
||||
auto current_word = [](StringView s) {
|
||||
auto it = s.end();
|
||||
|
@ -785,7 +787,7 @@ void regex_prompt(Context& context, String prompt, String default_regex, T func)
|
|||
[&](auto&& m) { candidates.push_back(m.candidate().str()); return true; });
|
||||
return {(int)(word.begin() - regex.begin()), pos, std::move(candidates) };
|
||||
},
|
||||
[=](StringView str, PromptEvent event, Context& context) mutable {
|
||||
[=, func=T(std::move(func))](StringView str, PromptEvent event, Context& context) mutable {
|
||||
try
|
||||
{
|
||||
if (event != PromptEvent::Change and context.has_client())
|
||||
|
@ -879,16 +881,12 @@ void search(Context& context, NormalParams params)
|
|||
const char reg = to_lower(params.reg ? params.reg : '/');
|
||||
const int count = params.count;
|
||||
|
||||
auto reg_content = RegisterManager::instance()[reg].get(context);
|
||||
Vector<String> saved_reg{reg_content.begin(), reg_content.end()};
|
||||
const int main_index = std::min(context.selections().main_index(), saved_reg.size()-1);
|
||||
|
||||
regex_prompt<regex_mode>(context, prompt.str(), saved_reg[main_index],
|
||||
[reg, count, saved_reg]
|
||||
regex_prompt<regex_mode>(context, prompt.str(), reg,
|
||||
[reg, count, saved_reg = RegisterManager::instance()[reg].save(context)]
|
||||
(const Regex& regex, PromptEvent event, Context& context) {
|
||||
if (event == PromptEvent::Abort)
|
||||
{
|
||||
RegisterManager::instance()[reg].set(context, saved_reg);
|
||||
RegisterManager::instance()[reg].restore(context, saved_reg);
|
||||
return;
|
||||
}
|
||||
RegisterManager::instance()[reg].set(context, regex.str());
|
||||
|
@ -907,7 +905,7 @@ template<SelectMode mode, RegexMode regex_mode>
|
|||
void search_next(Context& context, NormalParams params)
|
||||
{
|
||||
const char reg = to_lower(params.reg ? params.reg : '/');
|
||||
StringView str = context.main_sel_register_value(reg);
|
||||
StringView str = RegisterManager::instance()[reg].get(context).back();
|
||||
if (not str.empty())
|
||||
{
|
||||
Regex regex{str, direction_flags(regex_mode)};
|
||||
|
@ -942,25 +940,21 @@ void search_next(Context& context, NormalParams params)
|
|||
template<bool smart>
|
||||
void use_selection_as_search_pattern(Context& context, NormalParams params)
|
||||
{
|
||||
Vector<String> patterns;
|
||||
auto& sels = context.selections();
|
||||
const auto& buffer = context.buffer();
|
||||
for (auto& sel : sels)
|
||||
{
|
||||
auto& sel = context.selections().main();
|
||||
const auto beg = sel.min(), end = buffer.char_next(sel.max());
|
||||
patterns.push_back(format("{}{}{}",
|
||||
String pattern = format("{}{}{}",
|
||||
smart and is_bow(buffer, beg) ? "\\b" : "",
|
||||
escape(buffer.string(beg, end), "^$\\.*+?()[]{}|", '\\'),
|
||||
smart and is_eow(buffer, end) ? "\\b" : ""));
|
||||
}
|
||||
smart and is_eow(buffer, end) ? "\\b" : "");
|
||||
|
||||
const char reg = to_lower(params.reg ? params.reg : '/');
|
||||
|
||||
context.print_status({
|
||||
format("register '{}' set to '{}'", reg, fix_atom_text(patterns[sels.main_index()])),
|
||||
format("register '{}' set to '{}'", reg, fix_atom_text(pattern)),
|
||||
context.faces()["Information"] });
|
||||
|
||||
RegisterManager::instance()[reg].set(context, patterns);
|
||||
RegisterManager::instance()[reg].set(context, {pattern});
|
||||
|
||||
// Hack, as Window do not take register state into account
|
||||
if (context.has_window())
|
||||
|
@ -973,15 +967,12 @@ void select_regex(Context& context, NormalParams params)
|
|||
const int capture = params.count;
|
||||
auto prompt = capture ? format("select (capture {}):", capture) : "select:"_str;
|
||||
|
||||
auto reg_content = RegisterManager::instance()[reg].get(context);
|
||||
Vector<String> saved_reg{reg_content.begin(), reg_content.end()};
|
||||
const int main_index = std::min(context.selections().main_index(), saved_reg.size()-1);
|
||||
|
||||
regex_prompt(context, std::move(prompt), saved_reg[main_index],
|
||||
[reg, capture, saved_reg](Regex ex, PromptEvent event, Context& context) {
|
||||
regex_prompt(context, std::move(prompt), reg,
|
||||
[reg, capture, saved_reg = RegisterManager::instance()[reg].save(context)]
|
||||
(Regex ex, PromptEvent event, Context& context) {
|
||||
if (event == PromptEvent::Abort)
|
||||
{
|
||||
RegisterManager::instance()[reg].set(context, saved_reg);
|
||||
RegisterManager::instance()[reg].restore(context, saved_reg);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1000,15 +991,12 @@ void split_regex(Context& context, NormalParams params)
|
|||
const int capture = params.count;
|
||||
auto prompt = capture ? format("split (on capture {}):", (int)capture) : "split:"_str;
|
||||
|
||||
auto reg_content = RegisterManager::instance()[reg].get(context);
|
||||
Vector<String> saved_reg{reg_content.begin(), reg_content.end()};
|
||||
const int main_index = std::min(context.selections().main_index(), saved_reg.size()-1);
|
||||
|
||||
regex_prompt(context, std::move(prompt), saved_reg[main_index],
|
||||
[reg, capture, saved_reg](Regex ex, PromptEvent event, Context& context) {
|
||||
regex_prompt(context, std::move(prompt), reg,
|
||||
[reg, capture, saved_reg = RegisterManager::instance()[reg].save(context)]
|
||||
(Regex ex, PromptEvent event, Context& context) {
|
||||
if (event == PromptEvent::Abort)
|
||||
{
|
||||
RegisterManager::instance()[reg].set(context, saved_reg);
|
||||
RegisterManager::instance()[reg].restore(context, saved_reg);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1102,16 +1090,13 @@ void keep(Context& context, NormalParams params)
|
|||
constexpr StringView prompt = matching ? "keep matching:" : "keep not matching:";
|
||||
|
||||
const char reg = to_lower(params.reg ? params.reg : '/');
|
||||
auto saved_reg = RegisterManager::instance()[reg].get(context) | gather<Vector<String>>();
|
||||
const int main_index = std::min(context.selections().main_index(), saved_reg.size()-1);
|
||||
|
||||
regex_prompt(context, prompt.str(), saved_reg[main_index],
|
||||
[saved_reg, reg]
|
||||
regex_prompt(context, prompt.str(), reg,
|
||||
[reg, saved_reg = RegisterManager::instance()[reg].save(context)]
|
||||
(const Regex& regex, PromptEvent event, Context& context) {
|
||||
|
||||
if (event == PromptEvent::Abort)
|
||||
{
|
||||
RegisterManager::instance()[reg].set(context, saved_reg);
|
||||
RegisterManager::instance()[reg].restore(context, saved_reg);
|
||||
return;
|
||||
}
|
||||
if (not context.history_disabled())
|
||||
|
@ -1144,7 +1129,7 @@ void keep_pipe(Context& context, NormalParams)
|
|||
{
|
||||
context.input_handler().prompt(
|
||||
"keep pipe:", {}, {}, context.faces()["Prompt"],
|
||||
PromptFlags::DropHistoryEntriesWithBlankPrefix, shell_complete,
|
||||
PromptFlags::DropHistoryEntriesWithBlankPrefix, '|', shell_complete,
|
||||
[](StringView cmdline, PromptEvent event, Context& context) {
|
||||
if (event != PromptEvent::Validate)
|
||||
return;
|
||||
|
@ -1301,7 +1286,7 @@ void select_object(Context& context, NormalParams params)
|
|||
|
||||
context.input_handler().prompt(
|
||||
"object desc:", {}, {}, context.faces()["Prompt"],
|
||||
PromptFlags::None, complete_nothing,
|
||||
PromptFlags::None, '_', complete_nothing,
|
||||
[count,info](StringView cmdline, PromptEvent event, Context& context) {
|
||||
if (event != PromptEvent::Change)
|
||||
hide_auto_info_ifn(context, info);
|
||||
|
|
|
@ -20,6 +20,15 @@ public:
|
|||
|
||||
virtual void set(Context& context, ConstArrayView<String> values) = 0;
|
||||
virtual ConstArrayView<String> get(const Context& context) = 0;
|
||||
virtual const String& get_main(const Context& context, size_t main_index) = 0;
|
||||
|
||||
struct RestoreInfo
|
||||
{
|
||||
std::vector<String> data;
|
||||
size_t size;
|
||||
};
|
||||
virtual RestoreInfo save(const Context&) = 0;
|
||||
virtual void restore(Context&, const RestoreInfo&) = 0;
|
||||
};
|
||||
|
||||
// static value register, which can be modified
|
||||
|
@ -39,6 +48,25 @@ public:
|
|||
else
|
||||
return ConstArrayView<String>(m_content);
|
||||
}
|
||||
|
||||
const String& get_main(const Context& context, size_t main_index) override
|
||||
{
|
||||
return get(context)[std::min(main_index, m_content.size() - 1)];
|
||||
}
|
||||
|
||||
RestoreInfo save(const Context& context) override
|
||||
{
|
||||
//std::unique_ptr<String[]> data{new String[m_content.size()]};
|
||||
//std::copy_n(m_content.data(), m_content.size(), data.get());
|
||||
auto content = get(context);
|
||||
std::vector<String> data{content.begin(), content.end()};
|
||||
return {std::move(data), content.size()};
|
||||
}
|
||||
|
||||
void restore(Context&, const RestoreInfo& info) override
|
||||
{
|
||||
m_content.assign(info.data.begin(), info.data.begin() + info.size);
|
||||
}
|
||||
protected:
|
||||
Vector<String, MemoryDomain::Registers> m_content;
|
||||
};
|
||||
|
@ -63,11 +91,47 @@ public:
|
|||
return StaticRegister::get(context);
|
||||
}
|
||||
|
||||
void restore(Context& context, const RestoreInfo& info) override
|
||||
{
|
||||
set(context, info.data);
|
||||
}
|
||||
|
||||
private:
|
||||
Getter m_getter;
|
||||
Setter m_setter;
|
||||
};
|
||||
|
||||
// Register that is used to store some kind prompt history
|
||||
class HistoryRegister : public StaticRegister
|
||||
{
|
||||
public:
|
||||
void set(Context&, ConstArrayView<String> values) 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 String& get_main(const Context&, size_t) override
|
||||
{
|
||||
return m_content.empty() ? String::ms_empty : m_content.back();
|
||||
}
|
||||
|
||||
RestoreInfo save(const Context&) override
|
||||
{
|
||||
return {{}, m_content.size()};
|
||||
}
|
||||
|
||||
void restore(Context&, const RestoreInfo& info) override
|
||||
{
|
||||
if (info.size < m_content.size())
|
||||
m_content.resize(info.size);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Func>
|
||||
std::unique_ptr<Register> make_dyn_reg(Func func)
|
||||
{
|
||||
|
@ -93,6 +157,14 @@ public:
|
|||
{
|
||||
return ConstArrayView<String>(String::ms_empty);
|
||||
}
|
||||
|
||||
const String& get_main(const Context&, size_t) override
|
||||
{
|
||||
return String::ms_empty;
|
||||
}
|
||||
|
||||
RestoreInfo save(const Context&) override { return {}; }
|
||||
void restore(Context&, const RestoreInfo& info) override {}
|
||||
};
|
||||
|
||||
class RegisterManager : public Singleton<RegisterManager>
|
||||
|
|
Loading…
Reference in New Issue
Block a user