Make error messages more consistent

This commit is contained in:
Delapouite 2018-04-06 16:56:53 +02:00
parent 4ff0c58518
commit cb02186c77
17 changed files with 47 additions and 47 deletions

View File

@ -167,7 +167,7 @@ bool Buffer::set_name(String name)
void Buffer::throw_if_read_only() const void Buffer::throw_if_read_only() const
{ {
if (m_flags & Flags::ReadOnly) if (m_flags & Flags::ReadOnly)
throw runtime_error("Buffer is read-only"); throw runtime_error("buffer is read-only");
} }
void Buffer::update_display_name() void Buffer::update_display_name()

View File

@ -41,7 +41,7 @@ Buffer* BufferManager::create_buffer(String name, Buffer::Flags flags,
buffer->on_registered(); buffer->on_registered();
if (contains(m_buffer_trash, buffer)) if (contains(m_buffer_trash, buffer))
throw runtime_error{"Buffer got removed during its creation"}; throw runtime_error{"buffer got removed during its creation"};
return buffer; return buffer;
} }

View File

@ -191,7 +191,7 @@ Client& ClientManager::get_client(StringView name)
{ {
if (Client* client = get_client_ifp(name)) if (Client* client = get_client_ifp(name))
return *client; return *client;
throw runtime_error(format("no client named '{}'", name)); throw runtime_error(format("no such client: '{}'", name));
} }
void ClientManager::redraw_clients() const void ClientManager::redraw_clients() const

View File

@ -56,7 +56,7 @@ Color str_to_color(StringView color)
(unsigned char)(hval(color[6]) * 16 + hval(color[7])), (unsigned char)(hval(color[6]) * 16 + hval(color[7])),
(unsigned char)(hval(color[8]) * 16 + hval(color[9])) }; (unsigned char)(hval(color[8]) * 16 + hval(color[9])) };
throw runtime_error(format("Unable to parse color '{}'", color)); throw runtime_error(format("unable to parse color: '{}'", color));
return Color::Default; return Color::Default;
} }

View File

@ -382,7 +382,7 @@ String expand(StringView str, const Context& context,
struct command_not_found : runtime_error struct command_not_found : runtime_error
{ {
command_not_found(StringView name) command_not_found(StringView name)
: runtime_error(name + " : no such command") {} : runtime_error(format("no such command: '{}'", name)) {}
}; };
CommandManager::CommandMap::const_iterator CommandManager::CommandMap::const_iterator

View File

@ -191,7 +191,7 @@ Scope& get_scope(StringView scope, const Context& context)
{ {
if (auto s = get_scope_ifp(scope, context)) if (auto s = get_scope_ifp(scope, context))
return *s; return *s;
throw runtime_error(format("error: no such scope '{}'", scope)); throw runtime_error(format("no such scope: '{}'", scope));
} }
struct CommandDesc struct CommandDesc
@ -722,7 +722,7 @@ const CommandDesc add_highlighter_cmd = {
auto it = registry.find(name); auto it = registry.find(name);
if (it == registry.end()) if (it == registry.end())
throw runtime_error(format("No such highlighter factory '{}'", name)); throw runtime_error(format("no such highlighter factory: '{}'", name));
get_highlighter(context, path).add_child(it->value.factory(highlighter_params)); get_highlighter(context, path).add_child(it->value.factory(highlighter_params));
// TODO: better, this will fail if we touch scopes highlighters that impact multiple windows // TODO: better, this will fail if we touch scopes highlighters that impact multiple windows
@ -796,7 +796,7 @@ const CommandDesc add_hook_cmd = {
[](const ParametersParser& parser, Context& context, const ShellContext&) [](const ParametersParser& parser, Context& context, const ShellContext&)
{ {
if (not contains(hooks, parser[1])) if (not contains(hooks, parser[1]))
throw runtime_error{format("Unknown hook '{}'", parser[1])}; throw runtime_error{format("no such hook: '{}'", parser[1])};
Regex regex{parser[2], RegexCompileFlags::Optimize}; Regex regex{parser[2], RegexCompileFlags::Optimize};
const String& command = parser[3]; const String& command = parser[3];
@ -1053,7 +1053,7 @@ const CommandDesc alias_cmd = {
[](const ParametersParser& parser, Context& context, const ShellContext&) [](const ParametersParser& parser, Context& context, const ShellContext&)
{ {
if (not CommandManager::instance().command_defined(parser[2])) if (not CommandManager::instance().command_defined(parser[2]))
throw runtime_error(format("Command '{}' does not exist", parser[2])); throw runtime_error(format("no such command: '{}'", parser[2]));
AliasRegistry& aliases = get_scope(parser[0], context).aliases(); AliasRegistry& aliases = get_scope(parser[0], context).aliases();
aliases.add_alias(parser[1], parser[2]); aliases.add_alias(parser[1], parser[2]);
@ -1116,7 +1116,7 @@ KeymapMode parse_keymap_mode(StringView str, const KeymapManager::UserModeList&
auto it = find(user_modes, str); auto it = find(user_modes, str);
if (it == user_modes.end()) if (it == user_modes.end())
throw runtime_error(format("unknown keymap mode '{}'", str)); throw runtime_error(format("no such keymap mode: '{}'", str));
char offset = static_cast<char>(KeymapMode::FirstUserMode); char offset = static_cast<char>(KeymapMode::FirstUserMode);
return (KeymapMode)(std::distance(user_modes.begin(), it) + offset); return (KeymapMode)(std::distance(user_modes.begin(), it) + offset);
@ -1207,7 +1207,7 @@ const CommandDesc debug_cmd = {
} }
} }
else else
throw runtime_error(format("unknown debug command '{}'", parser[0])); throw runtime_error(format("no such debug command: '{}'", parser[0]));
} }
}; };
@ -1341,7 +1341,7 @@ const CommandDesc unset_option_cmd = {
{ {
auto& options = get_options(parser[0], context, parser[1]); auto& options = get_options(parser[0], context, parser[1]);
if (&options == &GlobalScope::instance().options()) if (&options == &GlobalScope::instance().options())
throw runtime_error("Cannot unset options in global scope"); throw runtime_error("cannot unset options in global scope");
options.unset_option(parser[1]); options.unset_option(parser[1]);
} }
}; };
@ -1423,7 +1423,7 @@ const CommandDesc declare_option_cmd = {
else if (parser[0] == "range-specs") else if (parser[0] == "range-specs")
opt = &reg.declare_option<TimestampedList<RangeAndString>>(parser[1], docstring, {}, flags); opt = &reg.declare_option<TimestampedList<RangeAndString>>(parser[1], docstring, {}, flags);
else else
throw runtime_error(format("unknown type {}", parser[0])); throw runtime_error(format("no such option type: '{}'", parser[0]));
if (parser.positional_count() == 3) if (parser.positional_count() == 3)
opt->set_from_string(parser[2]); opt->set_from_string(parser[2]);
@ -1555,7 +1555,7 @@ public:
} }
catch (runtime_error& e) catch (runtime_error& e)
{ {
write_to_debug_buffer(format("Could not restore register '{}': {}", write_to_debug_buffer(format("could not restore register '{}': {}",
m_name, e.what())); m_name, e.what()));
} }
} }
@ -1572,7 +1572,7 @@ void context_wrap(const ParametersParser& parser, Context& context, Func func)
if ((int)(bool)parser.get_switch("buffer") + if ((int)(bool)parser.get_switch("buffer") +
(int)(bool)parser.get_switch("client") + (int)(bool)parser.get_switch("client") +
(int)(bool)parser.get_switch("try-client") > 1) (int)(bool)parser.get_switch("try-client") > 1)
throw runtime_error{"Only one of -buffer, -client or -try-client can be specified"}; throw runtime_error{"only one of -buffer, -client or -try-client can be specified"};
const bool no_hooks = parser.get_switch("no-hooks") or context.hooks_disabled(); const bool no_hooks = parser.get_switch("no-hooks") or context.hooks_disabled();
const bool no_keymaps = not parser.get_switch("with-maps"); const bool no_keymaps = not parser.get_switch("with-maps");
@ -1662,7 +1662,7 @@ void context_wrap(const ParametersParser& parser, Context& context, Func func)
if (not draft) if (not draft)
{ {
if (&sels.buffer() != &c.buffer()) if (&sels.buffer() != &c.buffer())
throw runtime_error("the buffer has changed while iterating on selections"); throw runtime_error("buffer has changed while iterating on selections");
update_selections(new_sels, main, c.buffer(), timestamp); update_selections(new_sels, main, c.buffer(), timestamp);
timestamp = c.buffer().timestamp(); timestamp = c.buffer().timestamp();
@ -1952,7 +1952,7 @@ const CommandDesc info_cmd = {
else if (*placement == "below") else if (*placement == "below")
style = InfoStyle::InlineBelow; style = InfoStyle::InlineBelow;
else else
throw runtime_error(format("invalid placement '{}'", *placement)); throw runtime_error(format("invalid placement: '{}'", *placement));
} }
} }
auto title = parser.get_switch("title").value_or(StringView{}); auto title = parser.get_switch("title").value_or(StringView{});
@ -2036,7 +2036,7 @@ const CommandDesc rename_client_cmd = {
{ {
const String& name = parser[0]; const String& name = parser[0];
if (not all_of(name, is_identifier)) if (not all_of(name, is_identifier))
throw runtime_error{format("Invalid client name '{}'", name)}; throw runtime_error{format("invalid client name: '{}'", name)};
else if (ClientManager::instance().client_name_exists(name) and else if (ClientManager::instance().client_name_exists(name) and
context.name() != name) context.name() != name)
throw runtime_error{format("client name '{}' is not unique", name)}; throw runtime_error{format("client name '{}' is not unique", name)};
@ -2094,7 +2094,7 @@ const CommandDesc change_directory_cmd = {
{ {
StringView target = parser.positional_count() == 1 ? StringView{parser[0]} : "~"; StringView target = parser.positional_count() == 1 ? StringView{parser[0]} : "~";
if (chdir(parse_filename(target).c_str()) != 0) if (chdir(parse_filename(target).c_str()) != 0)
throw runtime_error(format("cannot change to directory '{}'", target)); throw runtime_error(format("unable to change to directory: '{}'", target));
for (auto& buffer : BufferManager::instance()) for (auto& buffer : BufferManager::instance())
buffer->update_display_name(); buffer->update_display_name();
} }
@ -2111,7 +2111,7 @@ const CommandDesc rename_session_cmd = {
[](const ParametersParser& parser, Context&, const ShellContext&) [](const ParametersParser& parser, Context&, const ShellContext&)
{ {
if (not Server::instance().rename_session(parser[0])) if (not Server::instance().rename_session(parser[0]))
throw runtime_error(format("Cannot rename current session: '{}' may be already in use", parser[0])); throw runtime_error(format("unable to rename current session: '{}' may be already in use", parser[0]));
} }
}; };

View File

@ -36,7 +36,7 @@ static Face parse_face(StringView facedesc)
case 'B': res.attributes |= Attribute::Blink; break; case 'B': res.attributes |= Attribute::Blink; break;
case 'd': res.attributes |= Attribute::Dim; break; case 'd': res.attributes |= Attribute::Dim; break;
case 'i': res.attributes |= Attribute::Italic; break; case 'i': res.attributes |= Attribute::Italic; break;
default: throw runtime_error(format("unknown face attribute '{}'", StringView{*attr_it})); default: throw runtime_error(format("no such face attribute: '{}'", StringView{*attr_it}));
} }
} }
} }

View File

@ -289,14 +289,14 @@ void write_buffer_to_file(Buffer& buffer, StringView filename, bool force)
if (::stat(zfilename, &st) == 0) if (::stat(zfilename, &st) == 0)
{ {
if (::chmod(zfilename, st.st_mode | S_IWUSR) < 0) if (::chmod(zfilename, st.st_mode | S_IWUSR) < 0)
throw runtime_error("couldn't change file permissions"); throw runtime_error("unable to change file permissions");
} }
else else
force = false; force = false;
} }
auto restore_mode = on_scope_end([&]{ auto restore_mode = on_scope_end([&]{
if (force and ::chmod(zfilename, st.st_mode) < 0) if (force and ::chmod(zfilename, st.st_mode) < 0)
throw runtime_error("couldn't restore file permissions"); throw runtime_error("unable to restore file permissions");
}); });
int fd = open(zfilename, O_CREAT | O_WRONLY | O_TRUNC, 0644); int fd = open(zfilename, O_CREAT | O_WRONLY | O_TRUNC, 0644);
@ -372,7 +372,7 @@ void make_directory(StringView dir, mode_t mode)
if (stat(dirname.zstr(), &st) == 0) if (stat(dirname.zstr(), &st) == 0)
{ {
if (not S_ISDIR(st.st_mode)) if (not S_ISDIR(st.st_mode))
throw runtime_error(format("Cannot make directory, '{}' exists but is not a directory", dirname)); throw runtime_error(format("cannot make directory, '{}' exists but is not a directory", dirname));
} }
else else
{ {

View File

@ -27,7 +27,7 @@ void HighlighterGroup::fill_unique_ids(Vector<StringView>& unique_ids) const
void HighlighterGroup::add_child(HighlighterAndId&& hl) void HighlighterGroup::add_child(HighlighterAndId&& hl)
{ {
if ((hl.second->passes() & passes()) != hl.second->passes()) if ((hl.second->passes() & passes()) != hl.second->passes())
throw runtime_error{"Cannot add that highlighter to this group, passes don't match"}; throw runtime_error{"cannot add that highlighter to this group, passes don't match"};
hl.first = replace(hl.first, "/", "<slash>"); hl.first = replace(hl.first, "/", "<slash>");

View File

@ -486,7 +486,7 @@ private:
HighlighterAndId create_dynamic_regex_highlighter(HighlighterParameters params) HighlighterAndId create_dynamic_regex_highlighter(HighlighterParameters params)
{ {
if (params.size() < 2) if (params.size() < 2)
throw runtime_error("Wrong parameter count"); throw runtime_error("wrong parameter count");
FacesSpec faces; FacesSpec faces;
for (auto& spec : params.subrange(1)) for (auto& spec : params.subrange(1))
@ -1030,7 +1030,7 @@ struct LineNumbersHighlighter : Highlighter
StringView separator = parser.get_switch("separator").value_or(""); StringView separator = parser.get_switch("separator").value_or("");
if (separator.length() > 10) if (separator.length() > 10)
throw runtime_error("Separator length is limited to 10 bytes"); throw runtime_error("separator length is limited to 10 bytes");
return {"number_lines", std::make_unique<LineNumbersHighlighter>((bool)parser.get_switch("relative"), (bool)parser.get_switch("hlcursor"), separator.str())}; return {"number_lines", std::make_unique<LineNumbersHighlighter>((bool)parser.get_switch("relative"), (bool)parser.get_switch("hlcursor"), separator.str())};
} }

View File

@ -1127,7 +1127,7 @@ public:
else if (key == Key::Escape or key == ctrl('c')) else if (key == Key::Escape or key == ctrl('c'))
{ {
if (m_in_end) if (m_in_end)
throw runtime_error("Asked to exit insert mode while running InsertEnd hook"); throw runtime_error("asked to exit insert mode while running InsertEnd hook");
m_in_end = true; m_in_end = true;
context().hooks().run_hook("InsertEnd", "", context()); context().hooks().run_hook("InsertEnd", "", context());

View File

@ -356,7 +356,7 @@ parse_json(const char* pos, const char* end)
throw runtime_error("unable to parse object, expected ',' or '}'"); throw runtime_error("unable to parse object, expected ',' or '}'");
} }
} }
throw runtime_error("Could not parse json"); throw runtime_error("unable to parse json");
} }
std::tuple<Value, const char*> std::tuple<Value, const char*>

View File

@ -111,10 +111,10 @@ KeyList parse_keys(StringView str)
if (val >= 1 and val <= 12) if (val >= 1 and val <= 12)
result.emplace_back(modifier, Key::F1 + (val - 1)); result.emplace_back(modifier, Key::F1 + (val - 1));
else else
throw runtime_error("Only F1 through F12 are supported"); throw runtime_error("only F1 through F12 are supported");
} }
else else
throw runtime_error("Failed to parse " + throw runtime_error("unable to parse " +
StringView{it.base(), end_it.base()+1}); StringView{it.base(), end_it.base()+1});
it = end_it; it = end_it;

View File

@ -1442,7 +1442,7 @@ void start_or_end_macro_recording(Context& context, NormalParams params)
{ {
const char reg = to_lower(params.reg ? params.reg : '@'); const char reg = to_lower(params.reg ? params.reg : '@');
if (not is_basic_alpha(reg) and reg != '@') if (not is_basic_alpha(reg) and reg != '@')
throw runtime_error("Macros can only use the '@' and alphabetic registers"); throw runtime_error("macros can only use the '@' and alphabetic registers");
context.input_handler().start_recording(reg); context.input_handler().start_recording(reg);
} }
} }
@ -1457,7 +1457,7 @@ void replay_macro(Context& context, NormalParams params)
{ {
const char reg = to_lower(params.reg ? params.reg : '@'); const char reg = to_lower(params.reg ? params.reg : '@');
if (not is_basic_alpha(reg) and reg != '@') if (not is_basic_alpha(reg) and reg != '@')
throw runtime_error("Macros can only use the '@' and alphabetic registers"); throw runtime_error("macros can only use the '@' and alphabetic registers");
static bool running_macros[27] = {}; static bool running_macros[27] = {};
const size_t idx = reg != '@' ? (size_t)(reg - 'a') : 26; const size_t idx = reg != '@' ? (size_t)(reg - 'a') : 26;
@ -1466,7 +1466,7 @@ void replay_macro(Context& context, NormalParams params)
ConstArrayView<String> reg_val = RegisterManager::instance()[reg].get(context); ConstArrayView<String> reg_val = RegisterManager::instance()[reg].get(context);
if (reg_val.empty() or reg_val[0].empty()) if (reg_val.empty() or reg_val[0].empty())
throw runtime_error(format("Register '{}' is empty", reg)); throw runtime_error(format("register '{}' is empty", reg));
running_macros[idx] = true; running_macros[idx] = true;
auto stop = on_scope_end([&]{ running_macros[idx] = false; }); auto stop = on_scope_end([&]{ running_macros[idx] = false; });
@ -1675,14 +1675,14 @@ SelectionList read_selections_from_register(char reg, Context& context)
auto content = RegisterManager::instance()[reg].get(context); auto content = RegisterManager::instance()[reg].get(context);
if (content.size() != 1) if (content.size() != 1)
throw runtime_error(format("Register {} does not contain a selections desc", reg)); throw runtime_error(format("register '{}' does not contain a selections desc", reg));
StringView desc = content[0]; StringView desc = content[0];
auto arobase = find(desc, '@'); auto arobase = find(desc, '@');
auto percent = find(desc, '%'); auto percent = find(desc, '%');
if (arobase == desc.end() or percent == desc.end()) if (arobase == desc.end() or percent == desc.end())
throw runtime_error(format("Register {} does not contain a selections desc", reg)); throw runtime_error(format("register '{}' does not contain a selections desc", reg));
Buffer& buffer = BufferManager::instance().get_buffer({arobase+1, percent}); Buffer& buffer = BufferManager::instance().get_buffer({arobase+1, percent});
size_t timestamp = str_to_int({percent + 1, desc.end()}); size_t timestamp = str_to_int({percent + 1, desc.end()});
@ -1691,7 +1691,7 @@ SelectionList read_selections_from_register(char reg, Context& context)
| transform(selection_from_string) | transform(selection_from_string)
| gather<Vector<Selection>>(); | gather<Vector<Selection>>();
if (sels.empty()) if (sels.empty())
throw runtime_error(format("Register {} contains an empty selection list", reg)); throw runtime_error(format("register '{}' contains an empty selection list", reg));
return {SelectionList::UnsortedTag{}, buffer, std::move(sels), timestamp}; return {SelectionList::UnsortedTag{}, buffer, std::move(sels), timestamp};
} }
@ -1719,7 +1719,7 @@ CombineOp key_to_combine_op(Key key)
case '+': return CombineOp::SelectLongest; case '+': return CombineOp::SelectLongest;
case '-': return CombineOp::SelectShortest; case '-': return CombineOp::SelectShortest;
} }
throw runtime_error{format("unknown combine operator '{}'", key.key)}; throw runtime_error{format("no such combine operator: '{}'", key.key)};
} }
void combine_selection(const Buffer& buffer, Selection& sel, const Selection& other, CombineOp op) void combine_selection(const Buffer& buffer, Selection& sel, const Selection& other, CombineOp op)
@ -1779,7 +1779,7 @@ void combine_selections(Context& context, SelectionList list, Func func)
else else
{ {
if (list.size() != sels.size()) if (list.size() != sels.size())
throw runtime_error{format("The two selection lists don't have the same number of elements ({} vs {})", throw runtime_error{format("the two selection lists don't have the same number of elements ({} vs {})",
list.size(), sels.size())}; list.size(), sels.size())};
for (int i = 0; i < list.size(); ++i) for (int i = 0; i < list.size(); ++i)
combine_selection(sels.buffer(), list[i], sels[i], op); combine_selection(sels.buffer(), list[i], sels[i], op);
@ -1994,7 +1994,7 @@ void remove_selection(Context& context, NormalParams p)
if (index >= selections.size()) if (index >= selections.size())
throw runtime_error{format("invalid selection index: {}", index)}; throw runtime_error{format("invalid selection index: {}", index)};
if (selections.size() == 1) if (selections.size() == 1)
throw runtime_error{"Cannot remove the last selection"}; throw runtime_error{"cannot remove the last selection"};
selections.remove(index); selections.remove(index);
selections.check_invariant(); selections.check_invariant();

View File

@ -539,7 +539,7 @@ static sockaddr_un session_addr(StringView session)
addr.sun_family = AF_UNIX; addr.sun_family = AF_UNIX;
auto slash_count = std::count(session.begin(), session.end(), '/'); auto slash_count = std::count(session.begin(), session.end(), '/');
if (slash_count > 1) if (slash_count > 1)
throw runtime_error{"Session names are either <user>/<name> or <name>"}; throw runtime_error{"session names are either <user>/<name> or <name>"};
else if (slash_count == 1) else if (slash_count == 1)
format_to(addr.sun_path, "{}/kakoune/{}", tmpdir(), session); format_to(addr.sun_path, "{}/kakoune/{}", tmpdir(), session);
else else
@ -757,7 +757,7 @@ private:
break; break;
} }
default: default:
write_to_debug_buffer("Invalid introduction message received"); write_to_debug_buffer("invalid introduction message received");
close(sock); close(sock);
Server::instance().remove_accepter(this); Server::instance().remove_accepter(this);
} }
@ -778,7 +778,7 @@ Server::Server(String session_name)
: m_session{std::move(session_name)} : m_session{std::move(session_name)}
{ {
if (not all_of(m_session, is_identifier)) if (not all_of(m_session, is_identifier))
throw runtime_error{format("Invalid session name '{}'", session_name)}; throw runtime_error{format("invalid session name: '{}'", session_name)};
int listen_sock = socket(AF_UNIX, SOCK_STREAM, 0); int listen_sock = socket(AF_UNIX, SOCK_STREAM, 0);
fcntl(listen_sock, F_SETFD, FD_CLOEXEC); fcntl(listen_sock, F_SETFD, FD_CLOEXEC);
@ -817,7 +817,7 @@ Server::Server(String session_name)
bool Server::rename_session(StringView name) bool Server::rename_session(StringView name)
{ {
if (not all_of(name, is_identifier)) if (not all_of(name, is_identifier))
throw runtime_error{format("Invalid session name '{}'", name)}; throw runtime_error{format("invalid session name: '{}'", name)};
String old_socket_file = format("{}/kakoune/{}/{}", tmpdir(), String old_socket_file = format("{}/kakoune/{}/{}", tmpdir(),
get_user_name(geteuid()), m_session); get_user_name(geteuid()), m_session);

View File

@ -900,7 +900,7 @@ Selection find_next_match(const Context& context, const Selection& sel, const Re
: find_prev(buffer, pos, matches, regex, wrapped); : find_prev(buffer, pos, matches, regex, wrapped);
if (not found or matches[0].first == buffer.end()) if (not found or matches[0].first == buffer.end())
throw runtime_error(format("'{}': no matches found", regex.str())); throw runtime_error(format("no matches found: '{}'", regex.str()));
CaptureList captures; CaptureList captures;
for (const auto& match : matches) for (const auto& match : matches)

View File

@ -287,13 +287,13 @@ void format_impl(StringView fmt, ArrayView<const StringView> params, AppendFunc
append(StringView{it, opening}); append(StringView{it, opening});
auto closing = std::find(opening, end, '}'); auto closing = std::find(opening, end, '}');
if (closing == end) if (closing == end)
throw runtime_error("Format string error, unclosed '{'"); throw runtime_error("format string error, unclosed '{'");
const int index = (closing == opening + 1) ? const int index = (closing == opening + 1) ?
implicitIndex : str_to_int({opening+1, closing}); implicitIndex : str_to_int({opening+1, closing});
if (index >= params.size()) if (index >= params.size())
throw runtime_error("Format string parameter index too big"); throw runtime_error("format string parameter index too big");
append(params[index]); append(params[index]);
implicitIndex = index+1; implicitIndex = index+1;