Use register to store prompt history

This commit is contained in:
Maxime Coste 2019-06-05 23:19:27 +10:00
parent a9e778fcc7
commit e613292568
7 changed files with 146 additions and 87 deletions

View File

@ -1739,10 +1739,10 @@ void context_wrap(const ParametersParser& parser, Context& context, StringView d
auto& register_manager = RegisterManager::instance(); auto& register_manager = RegisterManager::instance();
auto make_register_restorer = [&](char c) { 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 try
{ {
RegisterManager::instance()[c].set(context, save); RegisterManager::instance()[c].restore(context, save);
} }
catch (runtime_error& err) catch (runtime_error& err)
{ {
@ -2001,7 +2001,7 @@ const CommandDesc prompt_cmd = {
CapturedShellContext sc{shell_context}; CapturedShellContext sc{shell_context};
context.input_handler().prompt( context.input_handler().prompt(
parser[0], initstr.str(), {}, context.faces()["Prompt"], parser[0], initstr.str(), {}, context.faces()["Prompt"],
flags, std::move(completer), flags, '_', std::move(completer),
[=](StringView str, PromptEvent event, Context& context) mutable [=](StringView str, PromptEvent event, Context& context) mutable
{ {
if ((event == PromptEvent::Abort and on_abort.empty()) or if ((event == PromptEvent::Abort and on_abort.empty()) or

View File

@ -239,11 +239,8 @@ void Context::end_edition()
StringView Context::main_sel_register_value(StringView reg) const 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; size_t index = m_selections ? (*m_selections).main_index() : 0;
if (strings.size() <= index) return RegisterManager::instance()[reg].get_main(*this, index);
index = strings.size() - 1;
return strings[index];
} }
} }

View File

@ -751,11 +751,13 @@ class Prompt : public InputMode
public: public:
Prompt(InputHandler& input_handler, StringView prompt, Prompt(InputHandler& input_handler, StringView prompt,
String initstr, String emptystr, Face face, PromptFlags flags, 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)), : InputMode(input_handler), m_callback(std::move(callback)), m_completer(std::move(completer)),
m_prompt(prompt.str()), m_prompt_face(face), m_prompt(prompt.str()), m_prompt_face(face),
m_empty_text{std::move(emptystr)}, m_empty_text{std::move(emptystr)},
m_line_editor{context().faces()}, m_flags(flags), 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_auto_complete{context().options()["autocomplete"].get<AutoComplete>() & AutoComplete::Prompt},
m_idle_timer{TimePoint::max(), context().flags() & Context::Flags::Draft ? m_idle_timer{TimePoint::max(), context().flags() & Context::Flags::Draft ?
Timer::Callback{} : [this](Timer&) { Timer::Callback{} : [this](Timer&) {
@ -769,13 +771,11 @@ public:
context().hooks().run_hook(Hook::PromptIdle, "", context()); 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); m_line_editor.reset(std::move(initstr), m_empty_text);
} }
void on_key(Key key) override void on_key(Key key) override
{ {
History& history = ms_history[m_prompt];
const String& line = m_line_editor.line(); const String& line = m_line_editor.line();
if (key == Key::Return) if (key == Key::Return)
@ -790,7 +790,7 @@ public:
} }
if (not context().history_disabled()) if (not context().history_disabled())
history_push(history, line); history_push(line);
context().print_status(DisplayLine{}); context().print_status(DisplayLine{});
if (context().has_client()) if (context().has_client())
context().client().menu_hide(); context().client().menu_hide();
@ -807,7 +807,7 @@ public:
else if (key == Key::Escape or key == ctrl('c')) else if (key == Key::Escape or key == ctrl('c'))
{ {
if (not context().history_disabled()) if (not context().history_disabled())
history_push(history, line); history_push(line);
context().print_status(DisplayLine{}); context().print_status(DisplayLine{});
if (context().has_client()) if (context().has_client())
context().client().menu_hide(); context().client().menu_hide();
@ -853,22 +853,25 @@ public:
} }
else if (key == Key::Up or key == ctrl('p')) 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; m_prefix = line;
auto it = m_history_it; auto index = m_current_history;
// search for the previous history entry matching typed prefix // search for the previous history entry matching typed prefix
do do
{ {
--it; --index;
if (prefix_match(*it, m_prefix)) if (prefix_match(history[index], m_prefix))
{ {
m_history_it = it; m_current_history = index;
m_line_editor.reset(*it, m_empty_text); m_line_editor.reset(history[index], m_empty_text);
break; break;
} }
} while (it != history.begin()); } while (index != 0);
clear_completions(); clear_completions();
m_refresh_completion_pending = true; m_refresh_completion_pending = true;
@ -876,16 +879,19 @@ public:
} }
else if (key == Key::Down or key == ctrl('n')) // next 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 // search for the next history entry matching typed prefix
++m_history_it; ++m_current_history;
while (m_history_it != history.end() and while (m_current_history != history.size() and
not prefix_match(*m_history_it, m_prefix)) not prefix_match(history[m_current_history], m_prefix))
++m_history_it; ++m_current_history;
if (m_history_it != history.end()) if (m_current_history != history.size())
m_line_editor.reset(*m_history_it, m_empty_text); m_line_editor.reset(history[m_current_history], m_empty_text);
else else
m_line_editor.reset(m_prefix, m_empty_text); m_line_editor.reset(m_prefix, m_empty_text);
@ -1089,27 +1095,22 @@ private:
LineEditor m_line_editor; LineEditor m_line_editor;
bool m_line_changed = false; bool m_line_changed = false;
PromptFlags m_flags; PromptFlags m_flags;
Register& m_history;
size_t m_current_history;
bool m_auto_complete; bool m_auto_complete;
bool m_refresh_completion_pending = true; bool m_refresh_completion_pending = true;
Timer m_idle_timer; Timer m_idle_timer;
using History = Vector<String, MemoryDomain::History>; void history_push(StringView entry)
static HashMap<String, History, MemoryDomain::History> ms_history;
History::iterator m_history_it;
void history_push(History& history, StringView entry)
{ {
if (entry.empty() or if (entry.empty() or
(m_flags & PromptFlags::DropHistoryEntriesWithBlankPrefix and (m_flags & PromptFlags::DropHistoryEntriesWithBlankPrefix and
is_horizontal_blank(entry[0_byte]))) is_horizontal_blank(entry[0_byte])))
return; return;
history.erase(std::remove(history.begin(), history.end(), entry), m_history.set(context(), {entry.str()});
history.end());
history.push_back(entry.str());
} }
}; };
HashMap<String, Prompt::History, MemoryDomain::History> Prompt::ms_history;
class NextKey : public InputMode class NextKey : public InputMode
{ {
@ -1592,11 +1593,12 @@ void InputHandler::repeat_last_insert()
} }
void InputHandler::prompt(StringView prompt, String initstr, String emptystr, 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) PromptCompleter completer, PromptCallback callback)
{ {
push_mode(new InputModes::Prompt(*this, prompt, std::move(initstr), std::move(emptystr), 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) void InputHandler::set_prompt_face(Face prompt_face)

View File

@ -68,7 +68,7 @@ public:
// returns to normal mode after validation if callback does // returns to normal mode after validation if callback does
// not change the mode itself // not change the mode itself
void prompt(StringView prompt, String initstr, String emptystr, 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); PromptCompleter completer, PromptCallback callback);
void set_prompt_face(Face prompt_face); void set_prompt_face(Face prompt_face);

View File

@ -280,9 +280,12 @@ void register_registers()
{ {
RegisterManager& register_manager = RegisterManager::instance(); RegisterManager& register_manager = RegisterManager::instance();
for (auto c : "abcdefghijklmnopqrstuvwxyz/\"|^@:") for (auto c : StringView{"abcdefghijklmnopqrstuvwxyz\"^@"})
register_manager.add_register(c, std::make_unique<StaticRegister>()); 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>; using StringList = Vector<String, MemoryDomain::Registers>;
register_manager.add_register('%', make_dyn_reg( register_manager.add_register('%', make_dyn_reg(

View File

@ -456,6 +456,7 @@ void command(Context& context, EnvVarMap env_vars)
context.input_handler().prompt( context.input_handler().prompt(
":", {}, context.main_sel_register_value(':').str(), ":", {}, context.main_sel_register_value(':').str(),
context.faces()["Prompt"], PromptFlags::DropHistoryEntriesWithBlankPrefix, context.faces()["Prompt"], PromptFlags::DropHistoryEntriesWithBlankPrefix,
':',
[](const Context& context, CompletionFlags flags, [](const Context& context, CompletionFlags flags,
StringView cmd_line, ByteCount pos) { StringView cmd_line, ByteCount pos) {
return CommandManager::instance().complete(context, flags, cmd_line, 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:"; const char* prompt = replace ? "pipe:" : "pipe-to:";
context.input_handler().prompt( context.input_handler().prompt(
prompt, {}, context.main_sel_register_value("|").str(), context.faces()["Prompt"], prompt, {}, context.main_sel_register_value("|").str(), context.faces()["Prompt"],
PromptFlags::DropHistoryEntriesWithBlankPrefix, PromptFlags::DropHistoryEntriesWithBlankPrefix, '|',
shell_complete, shell_complete,
[](StringView cmdline, PromptEvent event, Context& context) [](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:"; const char* prompt = mode == InsertMode::Insert ? "insert-output:" : "append-output:";
context.input_handler().prompt( context.input_handler().prompt(
prompt, {}, context.main_sel_register_value("|").str(), context.faces()["Prompt"], prompt, {}, context.main_sel_register_value("|").str(), context.faces()["Prompt"],
PromptFlags::DropHistoryEntriesWithBlankPrefix, PromptFlags::DropHistoryEntriesWithBlankPrefix, '|',
shell_complete, shell_complete,
[](StringView cmdline, PromptEvent event, Context& context) [](StringView cmdline, PromptEvent event, Context& context)
{ {
@ -753,14 +754,15 @@ constexpr RegexCompileFlags direction_flags(RegexMode mode)
} }
template<RegexMode mode = RegexMode::Forward, typename T> 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)); static_assert(is_direction(mode));
DisplayCoord position = context.has_window() ? context.window().position() : DisplayCoord{}; DisplayCoord position = context.has_window() ? context.window().position() : DisplayCoord{};
SelectionList selections = context.selections(); SelectionList selections = context.selections();
auto default_regex = RegisterManager::instance()[reg].get_main(context, context.selections().main_index());
context.input_handler().prompt( context.input_handler().prompt(
std::move(prompt), {}, default_regex, context.faces()["Prompt"], std::move(prompt), {}, default_regex, context.faces()["Prompt"],
PromptFlags::Search, PromptFlags::Search, reg,
[](const Context& context, CompletionFlags, StringView regex, ByteCount pos) -> Completions { [](const Context& context, CompletionFlags, StringView regex, ByteCount pos) -> Completions {
auto current_word = [](StringView s) { auto current_word = [](StringView s) {
auto it = s.end(); 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; }); [&](auto&& m) { candidates.push_back(m.candidate().str()); return true; });
return {(int)(word.begin() - regex.begin()), pos, std::move(candidates) }; 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 try
{ {
if (event != PromptEvent::Change and context.has_client()) 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 char reg = to_lower(params.reg ? params.reg : '/');
const int count = params.count; const int count = params.count;
auto reg_content = RegisterManager::instance()[reg].get(context); regex_prompt<regex_mode>(context, prompt.str(), reg,
Vector<String> saved_reg{reg_content.begin(), reg_content.end()}; [reg, count, saved_reg = RegisterManager::instance()[reg].save(context)]
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]
(const Regex& regex, PromptEvent event, Context& context) { (const Regex& regex, PromptEvent event, Context& context) {
if (event == PromptEvent::Abort) if (event == PromptEvent::Abort)
{ {
RegisterManager::instance()[reg].set(context, saved_reg); RegisterManager::instance()[reg].restore(context, saved_reg);
return; return;
} }
RegisterManager::instance()[reg].set(context, regex.str()); RegisterManager::instance()[reg].set(context, regex.str());
@ -907,7 +905,7 @@ template<SelectMode mode, RegexMode regex_mode>
void search_next(Context& context, NormalParams params) void search_next(Context& context, NormalParams params)
{ {
const char reg = to_lower(params.reg ? params.reg : '/'); 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()) if (not str.empty())
{ {
Regex regex{str, direction_flags(regex_mode)}; Regex regex{str, direction_flags(regex_mode)};
@ -942,25 +940,21 @@ void search_next(Context& context, NormalParams params)
template<bool smart> template<bool smart>
void use_selection_as_search_pattern(Context& context, NormalParams params) void use_selection_as_search_pattern(Context& context, NormalParams params)
{ {
Vector<String> patterns;
auto& sels = context.selections();
const auto& buffer = context.buffer(); 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());
const auto beg = sel.min(), end = buffer.char_next(sel.max()); String pattern = format("{}{}{}",
patterns.push_back(format("{}{}{}", smart and is_bow(buffer, beg) ? "\\b" : "",
smart and is_bow(buffer, beg) ? "\\b" : "", escape(buffer.string(beg, end), "^$\\.*+?()[]{}|", '\\'),
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 : '/'); const char reg = to_lower(params.reg ? params.reg : '/');
context.print_status({ 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"] }); 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 // Hack, as Window do not take register state into account
if (context.has_window()) if (context.has_window())
@ -973,15 +967,12 @@ void select_regex(Context& context, NormalParams params)
const int capture = params.count; const int capture = params.count;
auto prompt = capture ? format("select (capture {}):", capture) : "select:"_str; auto prompt = capture ? format("select (capture {}):", capture) : "select:"_str;
auto reg_content = RegisterManager::instance()[reg].get(context); regex_prompt(context, std::move(prompt), reg,
Vector<String> saved_reg{reg_content.begin(), reg_content.end()}; [reg, capture, saved_reg = RegisterManager::instance()[reg].save(context)]
const int main_index = std::min(context.selections().main_index(), saved_reg.size()-1); (Regex ex, PromptEvent event, Context& context) {
regex_prompt(context, std::move(prompt), saved_reg[main_index],
[reg, capture, saved_reg](Regex ex, PromptEvent event, Context& context) {
if (event == PromptEvent::Abort) if (event == PromptEvent::Abort)
{ {
RegisterManager::instance()[reg].set(context, saved_reg); RegisterManager::instance()[reg].restore(context, saved_reg);
return; return;
} }
@ -1000,15 +991,12 @@ void split_regex(Context& context, NormalParams params)
const int capture = params.count; const int capture = params.count;
auto prompt = capture ? format("split (on capture {}):", (int)capture) : "split:"_str; auto prompt = capture ? format("split (on capture {}):", (int)capture) : "split:"_str;
auto reg_content = RegisterManager::instance()[reg].get(context); regex_prompt(context, std::move(prompt), reg,
Vector<String> saved_reg{reg_content.begin(), reg_content.end()}; [reg, capture, saved_reg = RegisterManager::instance()[reg].save(context)]
const int main_index = std::min(context.selections().main_index(), saved_reg.size()-1); (Regex ex, PromptEvent event, Context& context) {
regex_prompt(context, std::move(prompt), saved_reg[main_index],
[reg, capture, saved_reg](Regex ex, PromptEvent event, Context& context) {
if (event == PromptEvent::Abort) if (event == PromptEvent::Abort)
{ {
RegisterManager::instance()[reg].set(context, saved_reg); RegisterManager::instance()[reg].restore(context, saved_reg);
return; return;
} }
@ -1102,16 +1090,13 @@ void keep(Context& context, NormalParams params)
constexpr StringView prompt = matching ? "keep matching:" : "keep not matching:"; constexpr StringView prompt = matching ? "keep matching:" : "keep not matching:";
const char reg = to_lower(params.reg ? params.reg : '/'); 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], regex_prompt(context, prompt.str(), reg,
[saved_reg, reg] [reg, saved_reg = RegisterManager::instance()[reg].save(context)]
(const Regex& regex, PromptEvent event, Context& context) { (const Regex& regex, PromptEvent event, Context& context) {
if (event == PromptEvent::Abort) if (event == PromptEvent::Abort)
{ {
RegisterManager::instance()[reg].set(context, saved_reg); RegisterManager::instance()[reg].restore(context, saved_reg);
return; return;
} }
if (not context.history_disabled()) if (not context.history_disabled())
@ -1144,7 +1129,7 @@ void keep_pipe(Context& context, NormalParams)
{ {
context.input_handler().prompt( context.input_handler().prompt(
"keep pipe:", {}, {}, context.faces()["Prompt"], "keep pipe:", {}, {}, context.faces()["Prompt"],
PromptFlags::DropHistoryEntriesWithBlankPrefix, shell_complete, PromptFlags::DropHistoryEntriesWithBlankPrefix, '|', shell_complete,
[](StringView cmdline, PromptEvent event, Context& context) { [](StringView cmdline, PromptEvent event, Context& context) {
if (event != PromptEvent::Validate) if (event != PromptEvent::Validate)
return; return;
@ -1301,7 +1286,7 @@ void select_object(Context& context, NormalParams params)
context.input_handler().prompt( context.input_handler().prompt(
"object desc:", {}, {}, context.faces()["Prompt"], "object desc:", {}, {}, context.faces()["Prompt"],
PromptFlags::None, complete_nothing, PromptFlags::None, '_', complete_nothing,
[count,info](StringView cmdline, PromptEvent event, Context& context) { [count,info](StringView cmdline, PromptEvent event, Context& context) {
if (event != PromptEvent::Change) if (event != PromptEvent::Change)
hide_auto_info_ifn(context, info); hide_auto_info_ifn(context, info);

View File

@ -20,6 +20,15 @@ public:
virtual void set(Context& context, ConstArrayView<String> values) = 0; virtual void set(Context& context, ConstArrayView<String> values) = 0;
virtual ConstArrayView<String> get(const Context& context) = 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 // static value register, which can be modified
@ -39,6 +48,25 @@ public:
else else
return ConstArrayView<String>(m_content); 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: protected:
Vector<String, MemoryDomain::Registers> m_content; Vector<String, MemoryDomain::Registers> m_content;
}; };
@ -63,11 +91,47 @@ public:
return StaticRegister::get(context); return StaticRegister::get(context);
} }
void restore(Context& context, const RestoreInfo& info) override
{
set(context, info.data);
}
private: private:
Getter m_getter; Getter m_getter;
Setter m_setter; 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> template<typename Func>
std::unique_ptr<Register> make_dyn_reg(Func func) std::unique_ptr<Register> make_dyn_reg(Func func)
{ {
@ -93,6 +157,14 @@ public:
{ {
return ConstArrayView<String>(String::ms_empty); 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> class RegisterManager : public Singleton<RegisterManager>