src: De-indent docstrings passed to command/option/mapping definitions
This commit implements formatting behaviour when the first character of a docstring is a newline. In that case, the exact indentation level of the next line will be removed from that line and all subsequent non-empty lines. An error will be returned if a subsequent non-empty line does not have the same indentation level. The docstrings are always trimmed (surrounding whitespaces) whether the first character is a newline or not, as was the case prior to this commit. Example: the following declaration ``` define-command test -docstring %{ test: do something Nothing really. More indented lines. } nop ``` would be rendered as ``` test: do something Nothing really. More indented lines. ``` Related to #2405
This commit is contained in:
parent
e42c81c8eb
commit
da2f6c296a
|
@ -1163,9 +1163,9 @@ void define_command(const ParametersParser& parser, Context& context, const Shel
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
auto docstring = trim_whitespaces(parser.get_switch("docstring").value_or(StringView{}));
|
auto docstring = trim_indent(parser.get_switch("docstring").value_or(StringView{}));
|
||||||
|
|
||||||
cm.register_command(cmd_name, cmd, docstring.str(), desc, flags, CommandHelper{}, completer);
|
cm.register_command(cmd_name, cmd, docstring, desc, flags, CommandHelper{}, completer);
|
||||||
}
|
}
|
||||||
|
|
||||||
const CommandDesc define_command_cmd = {
|
const CommandDesc define_command_cmd = {
|
||||||
|
@ -1590,7 +1590,7 @@ const CommandDesc declare_option_cmd = {
|
||||||
if (parser.get_switch("hidden"))
|
if (parser.get_switch("hidden"))
|
||||||
flags = OptionFlags::Hidden;
|
flags = OptionFlags::Hidden;
|
||||||
|
|
||||||
auto docstring = trim_whitespaces(parser.get_switch("docstring").value_or(StringView{})).str();
|
auto docstring = trim_indent(parser.get_switch("docstring").value_or(StringView{}));
|
||||||
OptionsRegistry& reg = GlobalScope::instance().option_registry();
|
OptionsRegistry& reg = GlobalScope::instance().option_registry();
|
||||||
|
|
||||||
|
|
||||||
|
@ -1672,7 +1672,7 @@ const CommandDesc map_key_cmd = {
|
||||||
|
|
||||||
KeyList mapping = parse_keys(parser[3]);
|
KeyList mapping = parse_keys(parser[3]);
|
||||||
keymaps.map_key(key[0], keymap_mode, std::move(mapping),
|
keymaps.map_key(key[0], keymap_mode, std::move(mapping),
|
||||||
trim_whitespaces(parser.get_switch("docstring").value_or("")).str());
|
trim_indent(parser.get_switch("docstring").value_or("")));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,36 @@ StringView trim_whitespaces(StringView str)
|
||||||
return {beg, end};
|
return {beg, end};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String trim_indent(StringView str)
|
||||||
|
{
|
||||||
|
if (str.empty())
|
||||||
|
return {};
|
||||||
|
else if (str[0_byte] != '\n')
|
||||||
|
return trim_whitespaces(str).str();
|
||||||
|
|
||||||
|
str = str.substr(1_byte);
|
||||||
|
const CharCount docstring_length = str.char_length();
|
||||||
|
|
||||||
|
CharCount level_indent = 0;
|
||||||
|
while (level_indent < docstring_length
|
||||||
|
and is_horizontal_blank(str[level_indent]))
|
||||||
|
level_indent++;
|
||||||
|
|
||||||
|
if (level_indent >= docstring_length or not level_indent)
|
||||||
|
return trim_whitespaces(str).str();
|
||||||
|
|
||||||
|
const auto str_indent = str.substr(0, level_indent);
|
||||||
|
auto s = str | split<StringView>('\n') | transform([&](auto&& line) {
|
||||||
|
if (line.empty())
|
||||||
|
return line;
|
||||||
|
else if (not prefix_match(line, str_indent))
|
||||||
|
throw runtime_error("inconsistent indentation in the string");
|
||||||
|
|
||||||
|
return line.substr(str_indent.char_length());
|
||||||
|
});
|
||||||
|
|
||||||
|
return trim_whitespaces(join(s, '\n', false)).str();
|
||||||
|
}
|
||||||
|
|
||||||
String escape(StringView str, StringView characters, char escape)
|
String escape(StringView str, StringView characters, char escape)
|
||||||
{
|
{
|
||||||
|
@ -379,6 +409,14 @@ UnitTest test_string{[]()
|
||||||
kak_assert(wrapped2[1] == "unknown");
|
kak_assert(wrapped2[1] == "unknown");
|
||||||
kak_assert(wrapped2[2] == "type");
|
kak_assert(wrapped2[2] == "type");
|
||||||
|
|
||||||
|
kak_assert(trim_indent(" ") == "");
|
||||||
|
kak_assert(trim_indent("no-indent") == "no-indent");
|
||||||
|
kak_assert(trim_indent("\nno-indent") == "no-indent");
|
||||||
|
kak_assert(trim_indent("\n indent\n indent") == "indent\nindent");
|
||||||
|
kak_assert(trim_indent("\n indent\n indent") == "indent\n indent");
|
||||||
|
|
||||||
|
kak_expect_throw(runtime_error, trim_indent("\n indent\nno-indent"));
|
||||||
|
|
||||||
kak_assert(escape(R"(\youpi:matin:tchou\:)", ":\\", '\\') == R"(\\youpi\:matin\:tchou\\\:)");
|
kak_assert(escape(R"(\youpi:matin:tchou\:)", ":\\", '\\') == R"(\\youpi\:matin\:tchou\\\:)");
|
||||||
kak_assert(unescape(R"(\\youpi\:matin\:tchou\\\:)", ":\\", '\\') == R"(\youpi:matin:tchou\:)");
|
kak_assert(unescape(R"(\\youpi\:matin\:tchou\\\:)", ":\\", '\\') == R"(\youpi:matin:tchou\:)");
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@ namespace Kakoune
|
||||||
{
|
{
|
||||||
|
|
||||||
StringView trim_whitespaces(StringView str);
|
StringView trim_whitespaces(StringView str);
|
||||||
|
String trim_indent(StringView str);
|
||||||
|
|
||||||
String escape(StringView str, StringView characters, char escape);
|
String escape(StringView str, StringView characters, char escape);
|
||||||
String unescape(StringView str, StringView characters, char escape);
|
String unescape(StringView str, StringView characters, char escape);
|
||||||
|
|
Loading…
Reference in New Issue
Block a user