summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFrank LENORMAND <lenormf@gmail.com>2019-07-23 13:09:41 +0300
committerFrank LENORMAND <lenormf@gmail.com>2019-07-25 12:28:04 +0300
commitda2f6c296ae3ee280ffaca3937c6ae35c78d1e38 (patch)
tree99a0aebefcddaf2b109c2fc4eb7e29af9b52bd01
parente42c81c8eb53a6562f0d7313a6a9a92d8360238f (diff)
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
-rw-r--r--src/commands.cc8
-rw-r--r--src/string_utils.cc38
-rw-r--r--src/string_utils.hh1
3 files changed, 43 insertions, 4 deletions
diff --git a/src/commands.cc b/src/commands.cc
index 779e8d6d..443e4b62 100644
--- a/src/commands.cc
+++ b/src/commands.cc
@@ -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 = {
@@ -1590,7 +1590,7 @@ const CommandDesc declare_option_cmd = {
if (parser.get_switch("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();
@@ -1672,7 +1672,7 @@ const CommandDesc map_key_cmd = {
KeyList mapping = parse_keys(parser[3]);
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("")));
}
};
diff --git a/src/string_utils.cc b/src/string_utils.cc
index 3073a870..348526e9 100644
--- a/src/string_utils.cc
+++ b/src/string_utils.cc
@@ -19,6 +19,36 @@ StringView trim_whitespaces(StringView str)
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)
{
@@ -379,6 +409,14 @@ UnitTest test_string{[]()
kak_assert(wrapped2[1] == "unknown");
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(unescape(R"(\\youpi\:matin\:tchou\\\:)", ":\\", '\\') == R"(\youpi:matin:tchou\:)");
diff --git a/src/string_utils.hh b/src/string_utils.hh
index 928070d3..9c31d767 100644
--- a/src/string_utils.hh
+++ b/src/string_utils.hh
@@ -10,6 +10,7 @@ namespace Kakoune
{
StringView trim_whitespaces(StringView str);
+String trim_indent(StringView str);
String escape(StringView str, StringView characters, char escape);
String unescape(StringView str, StringView characters, char escape);