src highlighters: Factorise docstrings

Fixes #4367.
This commit is contained in:
Frank LENORMAND 2021-11-04 17:45:40 +03:00
parent 4eb8a100a8
commit 6cadffa090
3 changed files with 171 additions and 115 deletions

View File

@ -987,7 +987,19 @@ const CommandDesc add_highlighter_cmd = {
HighlighterRegistry& registry = HighlighterRegistry::instance(); HighlighterRegistry& registry = HighlighterRegistry::instance();
auto it = registry.find(params[1]); auto it = registry.find(params[1]);
if (it != registry.end()) if (it != registry.end())
return format("{}:\n{}", params[1], indent(it->value.docstring)); {
auto docstring = it->value.description->docstring;
auto desc_params = generate_switches_doc(it->value.description->params.switches);
if (desc_params.empty())
return format("{}:\n{}", params[1], indent(docstring));
else
{
auto desc_indent = Vector<String>{docstring, "Switches:", indent(desc_params)}
| transform([](auto& s) { return indent(s); });
return format("{}:\n{}", params[1], join(desc_indent, "\n"));
}
}
} }
return ""; return "";
}, },

View File

@ -10,6 +10,7 @@
#include "array_view.hh" #include "array_view.hh"
#include "string.hh" #include "string.hh"
#include "utils.hh" #include "utils.hh"
#include "parameters_parser.hh"
#include <memory> #include <memory>
@ -86,13 +87,19 @@ private:
using HighlighterParameters = ConstArrayView<String>; using HighlighterParameters = ConstArrayView<String>;
using HighlighterFactory = std::unique_ptr<Highlighter> (*)(HighlighterParameters params, Highlighter* parent); using HighlighterFactory = std::unique_ptr<Highlighter> (*)(HighlighterParameters params, Highlighter* parent);
struct HighlighterFactoryAndDocstring struct HighlighterDesc
{ {
HighlighterFactory factory; const char* docstring;
String docstring; ParameterDesc params;
}; };
struct HighlighterRegistry : HashMap<String, HighlighterFactoryAndDocstring, MemoryDomain::Highlight>, struct HighlighterFactoryAndDescription
{
HighlighterFactory factory;
const HighlighterDesc* description;
};
struct HighlighterRegistry : HashMap<String, HighlighterFactoryAndDescription, MemoryDomain::Highlight>,
Singleton<HighlighterRegistry> Singleton<HighlighterRegistry>
{}; {};

View File

@ -221,6 +221,10 @@ auto apply_face = [](const Face& face)
}; };
}; };
const HighlighterDesc fill_desc = {
"Fill the whole highlighted range with the given face",
{}
};
static std::unique_ptr<Highlighter> create_fill_highlighter(HighlighterParameters params, Highlighter*) static std::unique_ptr<Highlighter> create_fill_highlighter(HighlighterParameters params, Highlighter*)
{ {
if (params.size() != 1) if (params.size() != 1)
@ -253,6 +257,11 @@ private:
using FacesSpec = Vector<std::pair<size_t, String>, MemoryDomain::Highlight>; using FacesSpec = Vector<std::pair<size_t, String>, MemoryDomain::Highlight>;
const HighlighterDesc regex_desc = {
"Parameters: <regex> <capture num>:<face> <capture num>:<face>...\n"
"Highlights the matches for captures from the regex with the given faces",
{}
};
class RegexHighlighter : public Highlighter class RegexHighlighter : public Highlighter
{ {
public: public:
@ -479,6 +488,11 @@ private:
RegexHighlighter m_highlighter; RegexHighlighter m_highlighter;
}; };
const HighlighterDesc dynamic_regex_desc = {
"Parameters: <expr> <capture num>:<face> <capture num>:<face>...\n"
"Evaluate expression at every redraw to gather a regex",
{}
};
std::unique_ptr<Highlighter> create_dynamic_regex_highlighter(HighlighterParameters params, Highlighter*) std::unique_ptr<Highlighter> create_dynamic_regex_highlighter(HighlighterParameters params, Highlighter*)
{ {
if (params.size() < 2) if (params.size() < 2)
@ -544,6 +558,11 @@ std::unique_ptr<Highlighter> create_dynamic_regex_highlighter(HighlighterParamet
return make_hl(get_regex, get_face); return make_hl(get_regex, get_face);
} }
const HighlighterDesc line_desc = {
"Parameters: <value string> <face>\n"
"Highlight the line given by evaluating <value string> with <face>",
{}
};
std::unique_ptr<Highlighter> create_line_highlighter(HighlighterParameters params, Highlighter*) std::unique_ptr<Highlighter> create_line_highlighter(HighlighterParameters params, Highlighter*)
{ {
if (params.size() != 2) if (params.size() != 2)
@ -591,6 +610,11 @@ std::unique_ptr<Highlighter> create_line_highlighter(HighlighterParameters param
return make_highlighter(std::move(func)); return make_highlighter(std::move(func));
} }
const HighlighterDesc column_desc = {
"Parameters: <value string> <face>\n"
"Highlight the column given by evaluating <value string> with <face>",
{}
};
std::unique_ptr<Highlighter> create_column_highlighter(HighlighterParameters params, Highlighter*) std::unique_ptr<Highlighter> create_column_highlighter(HighlighterParameters params, Highlighter*)
{ {
if (params.size() != 2) if (params.size() != 2)
@ -651,6 +675,17 @@ std::unique_ptr<Highlighter> create_column_highlighter(HighlighterParameters par
return make_highlighter(std::move(func)); return make_highlighter(std::move(func));
} }
const HighlighterDesc wrap_desc = {
"Parameters: [-word] [-indent] [-width <max_width>] [-marker <marker_text>]\n"
"Wrap lines to window width",
{ {
{ "word", { false, "wrap at word boundaries instead of codepoint boundaries" } },
{ "indent", { false, "preserve line indentation of the wrapped line" } },
{ "width", { true, "wrap at the given column instead of the window's width" } },
{ "marker", { true, "insert the given text at the beginning of the wrapped line" } }, },
ParameterDesc::Flags::None, 0, 0
}
};
struct WrapHighlighter : Highlighter struct WrapHighlighter : Highlighter
{ {
WrapHighlighter(ColumnCount max_width, bool word_wrap, bool preserve_indent, String marker) WrapHighlighter(ColumnCount max_width, bool word_wrap, bool preserve_indent, String marker)
@ -915,14 +950,7 @@ struct WrapHighlighter : Highlighter
static std::unique_ptr<Highlighter> create(HighlighterParameters params, Highlighter*) static std::unique_ptr<Highlighter> create(HighlighterParameters params, Highlighter*)
{ {
static const ParameterDesc param_desc{ ParametersParser parser(params, wrap_desc.params);
{ { "word", { false, "" } },
{ "indent", { false, "" } },
{ "width", { true, "" } },
{ "marker", { true, "" } } },
ParameterDesc::Flags::None, 0, 0
};
ParametersParser parser(params, param_desc);
ColumnCount max_width = parser.get_switch("width").map(str_to_int) ColumnCount max_width = parser.get_switch("width").map(str_to_int)
.value_or(std::numeric_limits<int>::max()); .value_or(std::numeric_limits<int>::max());
@ -998,6 +1026,18 @@ struct TabulationHighlighter : Highlighter
} }
}; };
const HighlighterDesc show_whitespace_desc = {
"Parameters: [-tab <separator>] [-tabpad <separator>] [-lf <separator>] [-spc <separator>] [-nbsp <separator>]\n"
"Display whitespaces using symbols",
{ {
{ "tab", { true, "replace tabulations with the given character" } },
{ "tabpad", { true, "append as many of the given character as is necessary to honor `tabstop`" } },
{ "spc", { true, "replace spaces with the given character" } },
{ "lf", { true, "replace line feeds with the given character" } },
{ "nbsp", { true, "replace non-breakable spaces with the given character" } } },
ParameterDesc::Flags::None, 0, 0
}
};
struct ShowWhitespacesHighlighter : Highlighter struct ShowWhitespacesHighlighter : Highlighter
{ {
ShowWhitespacesHighlighter(String tab, String tabpad, String spc, String lf, String nbsp) ShowWhitespacesHighlighter(String tab, String tabpad, String spc, String lf, String nbsp)
@ -1007,15 +1047,7 @@ struct ShowWhitespacesHighlighter : Highlighter
static std::unique_ptr<Highlighter> create(HighlighterParameters params, Highlighter*) static std::unique_ptr<Highlighter> create(HighlighterParameters params, Highlighter*)
{ {
static const ParameterDesc param_desc{ ParametersParser parser(params, show_whitespace_desc.params);
{ { "tab", { true, "" } },
{ "tabpad", { true, "" } },
{ "spc", { true, "" } },
{ "lf", { true, "" } },
{ "nbsp", { true, "" } } },
ParameterDesc::Flags::None, 0, 0
};
ParametersParser parser(params, param_desc);
auto get_param = [&](StringView param, StringView fallback) { auto get_param = [&](StringView param, StringView fallback) {
StringView value = parser.get_switch(param).value_or(fallback); StringView value = parser.get_switch(param).value_or(fallback);
@ -1080,6 +1112,18 @@ private:
const String m_tab, m_tabpad, m_spc, m_lf, m_nbsp; const String m_tab, m_tabpad, m_spc, m_lf, m_nbsp;
}; };
const HighlighterDesc line_numbers_desc = {
"Parameters: [-relative] [-hlcursor] [-separators <separator|separator:cursor|cursor:up:down>] [-min-digits <cols>]\n"
"Display line numbers",
{ {
{ "relative", { false, "show line numbers relative to the main cursor line" } },
{ "separator", { true, "string to separate the line numbers column from the rest of the buffer (default '|')" } },
{ "cursor-separator", { true, "identical to -separator but applies only to the line of the cursor (default is the same value passed to -separator)" } },
{ "min-digits", { true, "use at least the given number of columns to display line numbers (default 2)" } },
{ "hlcursor", { false, "highlight the cursor line with a separate face" } } },
ParameterDesc::Flags::None, 0, 0
}
};
struct LineNumbersHighlighter : Highlighter struct LineNumbersHighlighter : Highlighter
{ {
LineNumbersHighlighter(bool relative, bool hl_cursor_line, String separator, String cursor_separator, int min_digits) LineNumbersHighlighter(bool relative, bool hl_cursor_line, String separator, String cursor_separator, int min_digits)
@ -1092,15 +1136,7 @@ struct LineNumbersHighlighter : Highlighter
static std::unique_ptr<Highlighter> create(HighlighterParameters params, Highlighter*) static std::unique_ptr<Highlighter> create(HighlighterParameters params, Highlighter*)
{ {
static const ParameterDesc param_desc{ ParametersParser parser(params, line_numbers_desc.params);
{ { "relative", { false, "" } },
{ "separator", { true, "" } },
{ "cursor-separator", { true, "" } },
{ "min-digits", { true, "" } },
{ "hlcursor", { false, "" } } },
ParameterDesc::Flags::None, 0, 0
};
ParametersParser parser(params, param_desc);
StringView separator = parser.get_switch("separator").value_or(""); StringView separator = parser.get_switch("separator").value_or("");
StringView cursor_separator = parser.get_switch("cursor-separator").value_or(separator); StringView cursor_separator = parser.get_switch("cursor-separator").value_or(separator);
@ -1192,6 +1228,10 @@ private:
constexpr StringView LineNumbersHighlighter::ms_id; constexpr StringView LineNumbersHighlighter::ms_id;
const HighlighterDesc show_matching_desc = {
"Apply the MatchingChar face to the char matching the one under the cursor",
{}
};
void show_matching_char(HighlightContext context, DisplayBuffer& display_buffer, BufferRange) void show_matching_char(HighlightContext context, DisplayBuffer& display_buffer, BufferRange)
{ {
const Face face = context.context.faces()["MatchingChar"]; const Face face = context.context.faces()["MatchingChar"];
@ -1364,6 +1404,11 @@ void option_list_postprocess(Vector<LineAndSpec, MemoryDomain::Options>& opt)
{ return std::get<0>(lhs) < std::get<0>(rhs); }); { return std::get<0>(lhs) < std::get<0>(rhs); });
} }
const HighlighterDesc flag_lines_desc = {
"Parameters: <face> <option name>\n"
"Display flags specified in the line-spec option <option name> with <face>",
{}
};
struct FlagLinesHighlighter : Highlighter struct FlagLinesHighlighter : Highlighter
{ {
FlagLinesHighlighter(String option_name, String default_face) FlagLinesHighlighter(String option_name, String default_face)
@ -1565,6 +1610,12 @@ bool option_add_from_strings(Vector<RangeAndString, MemoryDomain::Options>& opt,
return true; return true;
} }
const HighlighterDesc ranges_desc = {
"Parameters: <option name>\n"
"Use the range-specs option given as parameter to highlight buffer\n"
"each spec is interpreted as a face to apply to the range",
{}
};
struct RangesHighlighter : OptionBasedHighlighter<RangeAndStringList, RangesHighlighter> struct RangesHighlighter : OptionBasedHighlighter<RangeAndStringList, RangesHighlighter>
{ {
using RangesHighlighter::OptionBasedHighlighter::OptionBasedHighlighter; using RangesHighlighter::OptionBasedHighlighter::OptionBasedHighlighter;
@ -1589,6 +1640,12 @@ private:
} }
}; };
const HighlighterDesc replace_ranges_desc = {
"Parameters: <option name>\n"
"Use the range-specs option given as parameter to highlight buffer\n"
"each spec is interpreted as a display line to display in place of the range",
{}
};
struct ReplaceRangesHighlighter : OptionBasedHighlighter<RangeAndStringList, ReplaceRangesHighlighter, HighlightPass::Move> struct ReplaceRangesHighlighter : OptionBasedHighlighter<RangeAndStringList, ReplaceRangesHighlighter, HighlightPass::Move>
{ {
using ReplaceRangesHighlighter::OptionBasedHighlighter::OptionBasedHighlighter; using ReplaceRangesHighlighter::OptionBasedHighlighter::OptionBasedHighlighter;
@ -1682,18 +1739,34 @@ HighlightPass parse_passes(StringView str)
return passes; return passes;
} }
const HighlighterDesc higlighter_group_desc = {
"Parameters: [-passes <passes>]\n"
"Creates a group that can contain other highlighters",
{ {
{ "passes", { true, "flags(colorize|move|wrap) "
"kind of highlighters can be put in the group "
"(default colorize)" } } },
ParameterDesc::Flags::SwitchesOnlyAtStart, 0, 0
}
};
std::unique_ptr<Highlighter> create_highlighter_group(HighlighterParameters params, Highlighter*) std::unique_ptr<Highlighter> create_highlighter_group(HighlighterParameters params, Highlighter*)
{ {
static const ParameterDesc param_desc{ ParametersParser parser{params, higlighter_group_desc.params};
{ { "passes", { true, "" } } },
ParameterDesc::Flags::SwitchesOnlyAtStart, 0, 0
};
ParametersParser parser{params, param_desc};
HighlightPass passes = parse_passes(parser.get_switch("passes").value_or("colorize")); HighlightPass passes = parse_passes(parser.get_switch("passes").value_or("colorize"));
return std::make_unique<HighlighterGroup>(passes); return std::make_unique<HighlighterGroup>(passes);
} }
const HighlighterDesc ref_desc = {
"Parameters: [-passes <passes>] <path>\n"
"Reference the highlighter at <path> in shared highlighters",
{ {
{ "passes", { true, "flags(colorize|move|wrap) "
"kind of highlighters that can be referenced "
"(default colorize)" } } },
ParameterDesc::Flags::SwitchesOnlyAtStart, 1, 1
}
};
struct ReferenceHighlighter : Highlighter struct ReferenceHighlighter : Highlighter
{ {
ReferenceHighlighter(HighlightPass passes, String name) ReferenceHighlighter(HighlightPass passes, String name)
@ -1701,11 +1774,7 @@ struct ReferenceHighlighter : Highlighter
static std::unique_ptr<Highlighter> create(HighlighterParameters params, Highlighter*) static std::unique_ptr<Highlighter> create(HighlighterParameters params, Highlighter*)
{ {
static const ParameterDesc param_desc{ ParametersParser parser{params, ref_desc.params};
{ { "passes", { true, "" } } },
ParameterDesc::Flags::SwitchesOnlyAtStart, 1, 1
};
ParametersParser parser{params, param_desc};
HighlightPass passes = parse_passes(parser.get_switch("passes").value_or("colorize")); HighlightPass passes = parse_passes(parser.get_switch("passes").value_or("colorize"));
return std::make_unique<ReferenceHighlighter>(passes, parser[0]); return std::make_unique<ReferenceHighlighter>(passes, parser[0]);
} }
@ -1881,6 +1950,30 @@ struct RegionMatches : UseMemoryDomain<MemoryDomain::Highlight>
} }
}; };
const HighlighterDesc default_region_desc = {
"Parameters: <delegate_type> <delegate_params>...\n"
"Define the default region of a regions highlighter",
{}
};
const HighlighterDesc region_desc = {
"Parameters: [-match-capture] [-recurse <recurse>] <opening> <closing> <type> <params>...\n"
"Define a region for a regions highlighter, and apply the given delegate\n"
"highlighter as defined by <type> and eventual <params>...\n"
"The region starts at <begin> match and ends at the first <end>",
{ {
{ "match-capture", { false, "only consider region ending/recurse delimiters whose first capture group match the region beginning delimiter" } },
{ "recurse", { true, "make the region end on the first ending delimiter that does not close the given parameter" } } },
ParameterDesc::Flags::SwitchesOnlyAtStart | ParameterDesc::Flags::IgnoreUnknownSwitches,
3
}
};
const HighlighterDesc regions_desc = {
"Holds child region highlighters and segments the buffer in ranges based on those regions\n"
"definitions. The regions highlighter finds the next region to start by finding which\n"
"of its child region has the leftmost starting point from current position. In between\n"
"regions, the default-region child highlighter is applied (if such a child exists)",
{}
};
struct RegionsHighlighter : public Highlighter struct RegionsHighlighter : public Highlighter
{ {
public: public:
@ -2015,13 +2108,7 @@ public:
if (not is_regions(parent)) if (not is_regions(parent))
throw runtime_error{"region highlighter can only be added to a regions parent"}; throw runtime_error{"region highlighter can only be added to a regions parent"};
static const ParameterDesc param_desc{ ParametersParser parser{params, region_desc.params};
{ { "match-capture", { false, "" } }, { "recurse", { true, "" } } },
ParameterDesc::Flags::SwitchesOnlyAtStart | ParameterDesc::Flags::IgnoreUnknownSwitches,
3
};
ParametersParser parser{params, param_desc};
const bool match_capture = (bool)parser.get_switch("match-capture"); const bool match_capture = (bool)parser.get_switch("match-capture");
if (parser[0].empty() or parser[1].empty()) if (parser[0].empty() or parser[1].empty())
@ -2282,105 +2369,55 @@ void register_highlighters()
registry.insert({ registry.insert({
"column", "column",
{ create_column_highlighter, { create_column_highlighter, &column_desc } });
"Parameters: <value string> <face>\n"
"Highlight the column given by evaluating <value string> with <face>" } });
registry.insert({ registry.insert({
"default-region", "default-region",
{ RegionsHighlighter::create_default_region, { RegionsHighlighter::create_default_region, &default_region_desc } });
"Parameters: <delegate_type> <delegate_params>...\n"
"Define the default region of a regions highlighter" } });
registry.insert({ registry.insert({
"dynregex", "dynregex",
{ create_dynamic_regex_highlighter, { create_dynamic_regex_highlighter, &dynamic_regex_desc } });
"Parameters: <expr> <capture num>:<face> <capture num>:<face>...\n"
"Evaluate expression at every redraw to gather a regex" } });
registry.insert({ registry.insert({
"fill", "fill",
{ create_fill_highlighter, { create_fill_highlighter, &fill_desc } });
"Fill the whole highlighted range with the given face" } });
registry.insert({ registry.insert({
"flag-lines", "flag-lines",
{ FlagLinesHighlighter::create, { FlagLinesHighlighter::create, &flag_lines_desc } });
"Parameters: <face> <option name>\n"
"Display flags specified in the line-spec option <option name> with <face>"} });
registry.insert({ registry.insert({
"group", "group",
{ create_highlighter_group, { create_highlighter_group, &higlighter_group_desc } });
"Parameters: [-passes <passes>]\n"
"Creates a group that can contain other highlighters,\n"
"<passes> is a flags(colorize|move|wrap) defaulting to colorize\n"
"which specify what kind of highlighters can be put in the group" } });
registry.insert({ registry.insert({
"line", "line",
{ create_line_highlighter, { create_line_highlighter, &line_desc } });
"Parameters: <value string> <face>\n"
"Highlight the line given by evaluating <value string> with <face>" } });
registry.insert({ registry.insert({
"number-lines", "number-lines",
{ LineNumbersHighlighter::create, { LineNumbersHighlighter::create, &line_numbers_desc } });
"Display line numbers \n"
"Parameters: -relative, -hlcursor, -separator <separator text>, -cursor-separator <separator text>, -min-digits <cols>\n" } });
registry.insert({ registry.insert({
"ranges", "ranges",
{ RangesHighlighter::create, { RangesHighlighter::create, &ranges_desc } });
"Parameters: <option name>\n"
"Use the range-specs option given as parameter to highlight buffer\n"
"each spec is interpreted as a face to apply to the range\n" } });
registry.insert({ registry.insert({
"ref", "ref",
{ ReferenceHighlighter::create, { ReferenceHighlighter::create, &ref_desc } });
"Parameters: [-passes <passes>] <path>\n"
"Reference the highlighter at <path> in shared highlighters\n"
"<passes> is a flags(colorize|move|wrap) defaulting to colorize\n"
"which specify what kind of highlighters can be referenced" } });
registry.insert({ registry.insert({
"regex", "regex",
{ RegexHighlighter::create, { RegexHighlighter::create, &regex_desc } });
"Parameters: <regex> <capture num>:<face> <capture num>:<face>...\n"
"Highlights the matches for captures from the regex with the given faces" } });
registry.insert({ registry.insert({
"region", "region",
{ RegionsHighlighter::create_region, { RegionsHighlighter::create_region, &region_desc } });
"Parameters: [-match-capture] [-recurse <recurse>] <opening> <closing> <type> <params>...\n"
"Define a region for a regions highlighter, and apply the given delegate\n"
"highlighter as defined by <type> and eventual <params>...\n"
"The region starts at <begin> match and ends at the first <end>\n"
"If -recurse is specified, then the region ends at the first <end> that\n"
"does not close a <recurse> match.\n"
"If -match-capture is specified, then regions end/recurse matches must have\n"
"the same \\1 capture content as the begin match to be considered"} });
registry.insert({ registry.insert({
"regions", "regions",
{ RegionsHighlighter::create, { RegionsHighlighter::create, &regions_desc } });
"Parameters: None\n"
"Holds child region highlighters and segments the buffer in ranges based on those regions\n"
"definitions. The regions highlighter finds the next region to start by finding which\n"
"of its child region has the leftmost starting point from current position. In between\n"
"regions, the default-region child highlighter is applied (if such a child exists)" } });
registry.insert({ registry.insert({
"replace-ranges", "replace-ranges",
{ ReplaceRangesHighlighter::create, { ReplaceRangesHighlighter::create, &replace_ranges_desc } });
"Parameters: <option name>\n"
"Use the range-specs option given as parameter to highlight buffer\n"
"each spec is interpreted as a display line to display in place of the range\n" } });
registry.insert({ registry.insert({
"show-matching", "show-matching",
{ create_matching_char_highlighter, { create_matching_char_highlighter, &show_matching_desc } });
"Apply the MatchingChar face to the char matching the one under the cursor" } });
registry.insert({ registry.insert({
"show-whitespaces", "show-whitespaces",
{ ShowWhitespacesHighlighter::create, { ShowWhitespacesHighlighter::create, &show_whitespace_desc } });
"Display whitespaces using symbols \n"
"Parameters: -tab <separator> -tabpad <separator> -lf <separator> -spc <separator> -nbsp <separator>\n" } });
registry.insert({ registry.insert({
"wrap", "wrap",
{ WrapHighlighter::create, { WrapHighlighter::create, &wrap_desc } });
"Parameters: [-word] [-indent] [-width <max_width>] [-marker <marker_text>]\n"
"Wrap lines to window width, or max_width if given and window is wider,\n"
"wrap at word boundaries instead of codepoint boundaries if -word is given\n"
"insert marker_text at start of wrapped lines if given\n"
"preserve line indent in wrapped parts if -indent is given\n"} });
} }
} }