Switch highlight_spec_t to a struct

Rather than a janky bitmask, use a real struct with real fields.
This commit is contained in:
ridiculousfish 2019-03-03 17:34:00 -08:00
parent d165d1df27
commit 717ac9a8d5
7 changed files with 422 additions and 377 deletions

View File

@ -275,7 +275,7 @@ static std::string ansi_colorize(const wcstring &text,
assert(colors.size() == text.size());
outputter_t outp;
highlight_spec_t last_color = highlight_spec_normal;
highlight_spec_t last_color = highlight_role_t::normal;
for (size_t i = 0; i < text.size(); i++) {
highlight_spec_t color = colors.at(i);
if (color != last_color) {
@ -292,47 +292,47 @@ static std::string ansi_colorize(const wcstring &text,
/// for the various colors.
static const wchar_t *html_class_name_for_color(highlight_spec_t spec) {
#define P(x) L"fish_color_" #x
switch (spec & HIGHLIGHT_SPEC_PRIMARY_MASK) {
case highlight_spec_normal: {
switch (spec.foreground) {
case highlight_role_t::normal: {
return P(normal);
}
case highlight_spec_error: {
case highlight_role_t::error: {
return P(error);
}
case highlight_spec_command: {
case highlight_role_t::command: {
return P(command);
}
case highlight_spec_statement_terminator: {
case highlight_role_t::statement_terminator: {
return P(statement_terminator);
}
case highlight_spec_param: {
case highlight_role_t::param: {
return P(param);
}
case highlight_spec_comment: {
case highlight_role_t::comment: {
return P(comment);
}
case highlight_spec_match: {
case highlight_role_t::match: {
return P(match);
}
case highlight_spec_search_match: {
case highlight_role_t::search_match: {
return P(search_match);
}
case highlight_spec_operator: {
case highlight_role_t::operat: {
return P(operator);
}
case highlight_spec_escape: {
case highlight_role_t::escape: {
return P(escape);
}
case highlight_spec_quote: {
case highlight_role_t::quote: {
return P(quote);
}
case highlight_spec_redirection: {
case highlight_role_t::redirection: {
return P(redirection);
}
case highlight_spec_autosuggestion: {
case highlight_role_t::autosuggestion: {
return P(autosuggestion);
}
case highlight_spec_selection: {
case highlight_role_t::selection: {
return P(selection);
}
default: { return P(other); }
@ -347,7 +347,7 @@ static std::string html_colorize(const wcstring &text,
assert(colors.size() == text.size());
wcstring html = L"<pre><code>";
highlight_spec_t last_color = highlight_spec_normal;
highlight_spec_t last_color = highlight_role_t::normal;
for (size_t i = 0; i < text.size(); i++) {
// Handle colors.
highlight_spec_t color = colors.at(i);

View File

@ -4242,9 +4242,9 @@ static void test_highlighting() {
// Here are the components of our source and the colors we expect those to be.
struct highlight_component_t {
const wchar_t *txt;
int color;
highlight_spec_t color;
bool nospace;
highlight_component_t(const wchar_t *txt, int color, bool nospace = false)
highlight_component_t(const wchar_t *txt, highlight_spec_t color, bool nospace = false)
: txt(txt), color(color), nospace(nospace) {}
};
const bool ns = true;
@ -4252,182 +4252,184 @@ static void test_highlighting() {
using highlight_component_list_t = std::vector<highlight_component_t>;
std::vector<highlight_component_list_t> highlight_tests;
highlight_tests.push_back(
{{L"echo", highlight_spec_command},
{L"test/fish_highlight_test/foo", highlight_spec_param | highlight_modifier_valid_path},
{L"&", highlight_spec_statement_terminator}});
highlight_spec_t param_valid_path{highlight_role_t::param};
param_valid_path.valid_path = true;
highlight_tests.push_back({{L"echo", highlight_role_t::command},
{L"test/fish_highlight_test/foo", param_valid_path},
{L"&", highlight_role_t::statement_terminator}});
highlight_tests.push_back({
{L"command", highlight_spec_command},
{L"echo", highlight_spec_command},
{L"abc", highlight_spec_param},
{L"test/fish_highlight_test/foo", highlight_spec_param | highlight_modifier_valid_path},
{L"&", highlight_spec_statement_terminator},
{L"command", highlight_role_t::command},
{L"echo", highlight_role_t::command},
{L"abc", highlight_role_t::param},
{L"test/fish_highlight_test/foo", param_valid_path},
{L"&", highlight_role_t::statement_terminator},
});
highlight_tests.push_back({
{L"if command ls", highlight_spec_command},
{L"; ", highlight_spec_statement_terminator},
{L"echo", highlight_spec_command},
{L"abc", highlight_spec_param},
{L"; ", highlight_spec_statement_terminator},
{L"/bin/definitely_not_a_command", highlight_spec_error},
{L"; ", highlight_spec_statement_terminator},
{L"end", highlight_spec_command},
{L"if command ls", highlight_role_t::command},
{L"; ", highlight_role_t::statement_terminator},
{L"echo", highlight_role_t::command},
{L"abc", highlight_role_t::param},
{L"; ", highlight_role_t::statement_terminator},
{L"/bin/definitely_not_a_command", highlight_role_t::error},
{L"; ", highlight_role_t::statement_terminator},
{L"end", highlight_role_t::command},
});
// Verify that cd shows errors for non-directories.
highlight_tests.push_back({
{L"cd", highlight_spec_command},
{L"test/fish_highlight_test", highlight_spec_param | highlight_modifier_valid_path},
{L"cd", highlight_role_t::command},
{L"test/fish_highlight_test", param_valid_path},
});
highlight_tests.push_back({
{L"cd", highlight_spec_command},
{L"test/fish_highlight_test/foo", highlight_spec_error},
{L"cd", highlight_role_t::command},
{L"test/fish_highlight_test/foo", highlight_role_t::error},
});
highlight_tests.push_back({
{L"cd", highlight_spec_command},
{L"--help", highlight_spec_param},
{L"-h", highlight_spec_param},
{L"definitely_not_a_directory", highlight_spec_error},
{L"cd", highlight_role_t::command},
{L"--help", highlight_role_t::param},
{L"-h", highlight_role_t::param},
{L"definitely_not_a_directory", highlight_role_t::error},
});
// Command substitutions.
highlight_tests.push_back({
{L"echo", highlight_spec_command},
{L"param1", highlight_spec_param},
{L"(", highlight_spec_operator},
{L"ls", highlight_spec_command},
{L"param2", highlight_spec_param},
{L")", highlight_spec_operator},
{L"|", highlight_spec_statement_terminator},
{L"cat", highlight_spec_command},
{L"echo", highlight_role_t::command},
{L"param1", highlight_role_t::param},
{L"(", highlight_role_t::operat},
{L"ls", highlight_role_t::command},
{L"param2", highlight_role_t::param},
{L")", highlight_role_t::operat},
{L"|", highlight_role_t::statement_terminator},
{L"cat", highlight_role_t::command},
});
// Redirections substitutions.
highlight_tests.push_back({
{L"echo", highlight_spec_command},
{L"param1", highlight_spec_param},
{L"echo", highlight_role_t::command},
{L"param1", highlight_role_t::param},
// Input redirection.
{L"<", highlight_spec_redirection},
{L"/bin/echo", highlight_spec_redirection},
{L"<", highlight_role_t::redirection},
{L"/bin/echo", highlight_role_t::redirection},
// Output redirection to a valid fd.
{L"1>&2", highlight_spec_redirection},
{L"1>&2", highlight_role_t::redirection},
// Output redirection to an invalid fd.
{L"2>&", highlight_spec_redirection},
{L"LOL", highlight_spec_error},
{L"2>&", highlight_role_t::redirection},
{L"LOL", highlight_role_t::error},
// Just a param, not a redirection.
{L"test/blah", highlight_spec_param},
{L"test/blah", highlight_role_t::param},
// Input redirection from directory.
{L"<", highlight_spec_redirection},
{L"test/", highlight_spec_error},
{L"<", highlight_role_t::redirection},
{L"test/", highlight_role_t::error},
// Output redirection to an invalid path.
{L"3>", highlight_spec_redirection},
{L"/not/a/valid/path/nope", highlight_spec_error},
{L"3>", highlight_role_t::redirection},
{L"/not/a/valid/path/nope", highlight_role_t::error},
// Output redirection to directory.
{L"3>", highlight_spec_redirection},
{L"test/nope/", highlight_spec_error},
{L"3>", highlight_role_t::redirection},
{L"test/nope/", highlight_role_t::error},
// Redirections to overflow fd.
{L"99999999999999999999>&2", highlight_spec_error},
{L"2>&", highlight_spec_redirection},
{L"99999999999999999999", highlight_spec_error},
{L"99999999999999999999>&2", highlight_role_t::error},
{L"2>&", highlight_role_t::redirection},
{L"99999999999999999999", highlight_role_t::error},
// Output redirection containing a command substitution.
{L"4>", highlight_spec_redirection},
{L"(", highlight_spec_operator},
{L"echo", highlight_spec_command},
{L"test/somewhere", highlight_spec_param},
{L")", highlight_spec_operator},
{L"4>", highlight_role_t::redirection},
{L"(", highlight_role_t::operat},
{L"echo", highlight_role_t::command},
{L"test/somewhere", highlight_role_t::param},
{L")", highlight_role_t::operat},
// Just another param.
{L"param2", highlight_spec_param},
{L"param2", highlight_role_t::param},
});
highlight_tests.push_back({
{L"end", highlight_spec_error},
{L";", highlight_spec_statement_terminator},
{L"if", highlight_spec_command},
{L"end", highlight_spec_error},
{L"end", highlight_role_t::error},
{L";", highlight_role_t::statement_terminator},
{L"if", highlight_role_t::command},
{L"end", highlight_role_t::error},
});
highlight_tests.push_back({
{L"echo", highlight_spec_command},
{L"'single_quote", highlight_spec_error},
{L"echo", highlight_role_t::command},
{L"'single_quote", highlight_role_t::error},
});
highlight_tests.push_back({
{L"echo", highlight_spec_command},
{L"$foo", highlight_spec_operator},
{L"\"", highlight_spec_quote},
{L"$bar", highlight_spec_operator},
{L"\"", highlight_spec_quote},
{L"$baz[", highlight_spec_operator},
{L"1 2..3", highlight_spec_param},
{L"]", highlight_spec_operator},
{L"echo", highlight_role_t::command},
{L"$foo", highlight_role_t::operat},
{L"\"", highlight_role_t::quote},
{L"$bar", highlight_role_t::operat},
{L"\"", highlight_role_t::quote},
{L"$baz[", highlight_role_t::operat},
{L"1 2..3", highlight_role_t::param},
{L"]", highlight_role_t::operat},
});
highlight_tests.push_back({
{L"for", highlight_spec_command},
{L"i", highlight_spec_param},
{L"in", highlight_spec_command},
{L"1 2 3", highlight_spec_param},
{L";", highlight_spec_statement_terminator},
{L"end", highlight_spec_command},
{L"for", highlight_role_t::command},
{L"i", highlight_role_t::param},
{L"in", highlight_role_t::command},
{L"1 2 3", highlight_role_t::param},
{L";", highlight_role_t::statement_terminator},
{L"end", highlight_role_t::command},
});
highlight_tests.push_back({
{L"echo", highlight_spec_command},
{L"$$foo[", highlight_spec_operator},
{L"1", highlight_spec_param},
{L"][", highlight_spec_operator},
{L"2", highlight_spec_param},
{L"]", highlight_spec_operator},
{L"[3]", highlight_spec_param}, // two dollar signs, so last one is not an expansion
{L"echo", highlight_role_t::command},
{L"$$foo[", highlight_role_t::operat},
{L"1", highlight_role_t::param},
{L"][", highlight_role_t::operat},
{L"2", highlight_role_t::param},
{L"]", highlight_role_t::operat},
{L"[3]", highlight_role_t::param}, // two dollar signs, so last one is not an expansion
});
highlight_tests.push_back({
{L"cat", highlight_spec_command},
{L"/dev/null", highlight_spec_param},
{L"|", highlight_spec_statement_terminator},
{L"cat", highlight_role_t::command},
{L"/dev/null", highlight_role_t::param},
{L"|", highlight_role_t::statement_terminator},
// This is bogus, but we used to use "less" here and that doesn't have to be installed.
{L"cat", highlight_spec_command},
{L"2>", highlight_spec_redirection},
{L"cat", highlight_role_t::command},
{L"2>", highlight_role_t::redirection},
});
highlight_tests.push_back({
{L"if", highlight_spec_command},
{L"true", highlight_spec_command},
{L"&&", highlight_spec_operator},
{L"false", highlight_spec_command},
{L";", highlight_spec_statement_terminator},
{L"or", highlight_spec_operator},
{L"false", highlight_spec_command},
{L"||", highlight_spec_operator},
{L"true", highlight_spec_command},
{L";", highlight_spec_statement_terminator},
{L"and", highlight_spec_operator},
{L"not", highlight_spec_operator},
{L"!", highlight_spec_operator},
{L"true", highlight_spec_command},
{L";", highlight_spec_statement_terminator},
{L"end", highlight_spec_command},
{L"if", highlight_role_t::command},
{L"true", highlight_role_t::command},
{L"&&", highlight_role_t::operat},
{L"false", highlight_role_t::command},
{L";", highlight_role_t::statement_terminator},
{L"or", highlight_role_t::operat},
{L"false", highlight_role_t::command},
{L"||", highlight_role_t::operat},
{L"true", highlight_role_t::command},
{L";", highlight_role_t::statement_terminator},
{L"and", highlight_role_t::operat},
{L"not", highlight_role_t::operat},
{L"!", highlight_role_t::operat},
{L"true", highlight_role_t::command},
{L";", highlight_role_t::statement_terminator},
{L"end", highlight_role_t::command},
});
highlight_tests.push_back({
{L"echo", highlight_spec_command},
{L"%self", highlight_spec_operator},
{L"not%self", highlight_spec_param},
{L"self%not", highlight_spec_param},
{L"echo", highlight_role_t::command},
{L"%self", highlight_role_t::operat},
{L"not%self", highlight_role_t::param},
{L"self%not", highlight_role_t::param},
});
auto &vars = parser_t::principal_parser().vars();
@ -4435,21 +4437,21 @@ static void test_highlighting() {
vars.set(L"VARIABLE_IN_COMMAND", ENV_LOCAL, {L"a"});
vars.set(L"VARIABLE_IN_COMMAND2", ENV_LOCAL, {L"at"});
highlight_tests.push_back(
{{L"/bin/ca", highlight_spec_command, ns}, {L"*", highlight_spec_operator, ns}});
{{L"/bin/ca", highlight_role_t::command, ns}, {L"*", highlight_role_t::operat, ns}});
highlight_tests.push_back({{L"/bin/c", highlight_spec_command, ns},
{L"{$VARIABLE_IN_COMMAND}", highlight_spec_operator, ns},
{L"*", highlight_spec_operator, ns}});
highlight_tests.push_back({{L"/bin/c", highlight_role_t::command, ns},
{L"{$VARIABLE_IN_COMMAND}", highlight_role_t::operat, ns},
{L"*", highlight_role_t::operat, ns}});
highlight_tests.push_back({{L"/bin/c", highlight_spec_command, ns},
{L"{$VARIABLE_IN_COMMAND}", highlight_spec_operator, ns},
{L"*", highlight_spec_operator, ns}});
highlight_tests.push_back({{L"/bin/c", highlight_role_t::command, ns},
{L"{$VARIABLE_IN_COMMAND}", highlight_role_t::operat, ns},
{L"*", highlight_role_t::operat, ns}});
highlight_tests.push_back({{L"/bin/c", highlight_spec_command, ns},
{L"$VARIABLE_IN_COMMAND2", highlight_spec_operator, ns}});
highlight_tests.push_back({{L"/bin/c", highlight_role_t::command, ns},
{L"$VARIABLE_IN_COMMAND2", highlight_role_t::operat, ns}});
highlight_tests.push_back({{L"$EMPTY_VARIABLE", highlight_spec_error}});
highlight_tests.push_back({{L"\"$EMPTY_VARIABLE\"", highlight_spec_error}});
highlight_tests.push_back({{L"$EMPTY_VARIABLE", highlight_role_t::error}});
highlight_tests.push_back({{L"\"$EMPTY_VARIABLE\"", highlight_role_t::error}});
for (const highlight_component_list_t &components : highlight_tests) {
// Generate the text.
@ -4458,7 +4460,7 @@ static void test_highlighting() {
for (const highlight_component_t &comp : components) {
if (!text.empty() && !comp.nospace) {
text.push_back(L' ');
expected_colors.push_back(0);
expected_colors.push_back(highlight_spec_t{});
}
text.append(comp.txt);
expected_colors.resize(text.size(), comp.color);

View File

@ -39,71 +39,127 @@ namespace g = grammar;
#define CURSOR_POSITION_INVALID ((size_t)(-1))
/// Number of elements in the highlight_var array.
#define VAR_COUNT (sizeof(highlight_var) / sizeof(wchar_t *))
static const wchar_t *const highlight_var[] = {
[highlight_spec_normal] = L"fish_color_normal",
[highlight_spec_error] = L"fish_color_error",
[highlight_spec_command] = L"fish_color_command",
[highlight_spec_statement_terminator] = L"fish_color_end",
[highlight_spec_param] = L"fish_color_param",
[highlight_spec_comment] = L"fish_color_comment",
[highlight_spec_match] = L"fish_color_match",
[highlight_spec_search_match] = L"fish_color_search_match",
[highlight_spec_operator] = L"fish_color_operator",
[highlight_spec_escape] = L"fish_color_escape",
[highlight_spec_quote] = L"fish_color_quote",
[highlight_spec_redirection] = L"fish_color_redirection",
[highlight_spec_autosuggestion] = L"fish_color_autosuggestion",
[highlight_spec_selection] = L"fish_color_selection",
[highlight_spec_pager_progress] = L"fish_pager_color_progress",
[highlight_spec_pager_background] = L"fish_pager_color_background",
[highlight_spec_pager_prefix] = L"fish_pager_color_prefix",
[highlight_spec_pager_completion] = L"fish_pager_color_completion",
[highlight_spec_pager_description] = L"fish_pager_color_description",
[highlight_spec_pager_secondary_background] = L"fish_pager_color_secondary_background",
[highlight_spec_pager_secondary_prefix] = L"fish_pager_color_secondary_prefix",
[highlight_spec_pager_secondary_completion] = L"fish_pager_color_secondary_completion",
[highlight_spec_pager_secondary_description] = L"fish_pager_color_secondary_description",
[highlight_spec_pager_selected_background] = L"fish_pager_color_selected_background",
[highlight_spec_pager_selected_prefix] = L"fish_pager_color_selected_prefix",
[highlight_spec_pager_selected_completion] = L"fish_pager_color_selected_completion",
[highlight_spec_pager_selected_description] = L"fish_pager_color_selected_description",
};
static_assert(VAR_COUNT == HIGHLIGHT_SPEC_MAX, "Every color spec has a corresponding env var");
static const wchar_t *get_highlight_var_name(highlight_role_t role) {
switch (role) {
case highlight_role_t::normal:
return L"fish_color_normal";
case highlight_role_t::error:
return L"fish_color_error";
case highlight_role_t::command:
return L"fish_color_command";
case highlight_role_t::statement_terminator:
return L"fish_color_end";
case highlight_role_t::param:
return L"fish_color_param";
case highlight_role_t::comment:
return L"fish_color_comment";
case highlight_role_t::match:
return L"fish_color_match";
case highlight_role_t::search_match:
return L"fish_color_search_match";
case highlight_role_t::operat:
return L"fish_color_operator";
case highlight_role_t::escape:
return L"fish_color_escape";
case highlight_role_t::quote:
return L"fish_color_quote";
case highlight_role_t::redirection:
return L"fish_color_redirection";
case highlight_role_t::autosuggestion:
return L"fish_color_autosuggestion";
case highlight_role_t::selection:
return L"fish_color_selection";
case highlight_role_t::pager_progress:
return L"fish_pager_color_progress";
case highlight_role_t::pager_background:
return L"fish_pager_color_background";
case highlight_role_t::pager_prefix:
return L"fish_pager_color_prefix";
case highlight_role_t::pager_completion:
return L"fish_pager_color_completion";
case highlight_role_t::pager_description:
return L"fish_pager_color_description";
case highlight_role_t::pager_secondary_background:
return L"fish_pager_color_secondary_background";
case highlight_role_t::pager_secondary_prefix:
return L"fish_pager_color_secondary_prefix";
case highlight_role_t::pager_secondary_completion:
return L"fish_pager_color_secondary_completion";
case highlight_role_t::pager_secondary_description:
return L"fish_pager_color_secondary_description";
case highlight_role_t::pager_selected_background:
return L"fish_pager_color_selected_background";
case highlight_role_t::pager_selected_prefix:
return L"fish_pager_color_selected_prefix";
case highlight_role_t::pager_selected_completion:
return L"fish_pager_color_selected_completion";
case highlight_role_t::pager_selected_description:
return L"fish_pager_color_selected_description";
}
DIE("invalid highlight role");
}
// Table used to fetch fallback highlights in case the specified one
// wasn't set.
static const highlight_spec_t fallbacks[] = {
[highlight_spec_normal] = highlight_spec_normal,
[highlight_spec_error] = highlight_spec_normal,
[highlight_spec_command] = highlight_spec_normal,
[highlight_spec_statement_terminator] = highlight_spec_normal,
[highlight_spec_param] = highlight_spec_normal,
[highlight_spec_comment] = highlight_spec_normal,
[highlight_spec_match] = highlight_spec_normal,
[highlight_spec_search_match] = highlight_spec_normal,
[highlight_spec_operator] = highlight_spec_normal,
[highlight_spec_escape] = highlight_spec_normal,
[highlight_spec_quote] = highlight_spec_normal,
[highlight_spec_redirection] = highlight_spec_normal,
[highlight_spec_autosuggestion] = highlight_spec_normal,
[highlight_spec_selection] = highlight_spec_normal,
[highlight_spec_pager_progress] = highlight_spec_normal,
[highlight_spec_pager_background] = highlight_spec_normal,
[highlight_spec_pager_prefix] = highlight_spec_normal,
[highlight_spec_pager_completion] = highlight_spec_normal,
[highlight_spec_pager_description] = highlight_spec_normal,
[highlight_spec_pager_secondary_background] = highlight_spec_pager_background,
[highlight_spec_pager_secondary_prefix] = highlight_spec_pager_prefix,
[highlight_spec_pager_secondary_completion] = highlight_spec_pager_completion,
[highlight_spec_pager_secondary_description] = highlight_spec_pager_description,
[highlight_spec_pager_selected_background] = highlight_spec_search_match,
[highlight_spec_pager_selected_prefix] = highlight_spec_pager_prefix,
[highlight_spec_pager_selected_completion] = highlight_spec_pager_completion,
[highlight_spec_pager_selected_description] = highlight_spec_pager_description,
};
static_assert(sizeof(fallbacks) / sizeof(fallbacks[0]) == HIGHLIGHT_SPEC_MAX, "No missing fallbacks");
static highlight_role_t get_fallback(highlight_role_t role) {
switch (role) {
case highlight_role_t::normal:
return highlight_role_t::normal;
case highlight_role_t::error:
return highlight_role_t::normal;
case highlight_role_t::command:
return highlight_role_t::normal;
case highlight_role_t::statement_terminator:
return highlight_role_t::normal;
case highlight_role_t::param:
return highlight_role_t::normal;
case highlight_role_t::comment:
return highlight_role_t::normal;
case highlight_role_t::match:
return highlight_role_t::normal;
case highlight_role_t::search_match:
return highlight_role_t::normal;
case highlight_role_t::operat:
return highlight_role_t::normal;
case highlight_role_t::escape:
return highlight_role_t::normal;
case highlight_role_t::quote:
return highlight_role_t::normal;
case highlight_role_t::redirection:
return highlight_role_t::normal;
case highlight_role_t::autosuggestion:
return highlight_role_t::normal;
case highlight_role_t::selection:
return highlight_role_t::normal;
case highlight_role_t::pager_progress:
return highlight_role_t::normal;
case highlight_role_t::pager_background:
return highlight_role_t::normal;
case highlight_role_t::pager_prefix:
return highlight_role_t::normal;
case highlight_role_t::pager_completion:
return highlight_role_t::normal;
case highlight_role_t::pager_description:
return highlight_role_t::normal;
case highlight_role_t::pager_secondary_background:
return highlight_role_t::pager_background;
case highlight_role_t::pager_secondary_prefix:
return highlight_role_t::pager_prefix;
case highlight_role_t::pager_secondary_completion:
return highlight_role_t::pager_completion;
case highlight_role_t::pager_secondary_description:
return highlight_role_t::pager_description;
case highlight_role_t::pager_selected_background:
return highlight_role_t::search_match;
case highlight_role_t::pager_selected_prefix:
return highlight_role_t::pager_prefix;
case highlight_role_t::pager_selected_completion:
return highlight_role_t::pager_completion;
case highlight_role_t::pager_selected_description:
return highlight_role_t::pager_description;
}
DIE("invalid highlight role");
}
/// Determine if the filesystem containing the given fd is case insensitive for lookups regardless
/// of whether it preserves the case when saving a pathname.
@ -292,30 +348,19 @@ static bool plain_statement_get_expanded_command(const wcstring &src,
return err == EXPAND_OK || err == EXPAND_WILDCARD_MATCH;
}
rgb_color_t highlight_get_color(highlight_spec_t highlight, bool is_background) {
rgb_color_t highlight_get_color(const highlight_spec_t &highlight, bool is_background) {
// TODO: rationalize this principal_vars.
const auto &vars = env_stack_t::principal();
rgb_color_t result = rgb_color_t::normal();
highlight_role_t role = is_background ? highlight.background : highlight.foreground;
bool treat_as_background = is_background;
// Get the primary variable.
size_t idx = highlight_get_primary(highlight);
if (idx >= VAR_COUNT) {
return rgb_color_t::normal();
}
auto var = vars.get(highlight_var[idx]);
// debug( 1, L"%d -> %d -> %ls", highlight, idx, val );
if (!var) var = vars.get(highlight_var[fallbacks[idx]]);
if (!var) var = vars.get(highlight_var[0]);
if (var) result = parse_color(*var, treat_as_background);
auto var = vars.get(get_highlight_var_name(role));
if (!var) var = vars.get(get_highlight_var_name(get_fallback(role)));
if (!var) var = vars.get(get_highlight_var_name(highlight_role_t::normal));
if (var) result = parse_color(*var, is_background);
// Handle modifiers.
if (highlight & highlight_modifier_valid_path) {
if (!is_background && highlight.valid_path) {
auto var2 = vars.get(L"fish_color_valid_path");
if (var2) {
rgb_color_t result2 = parse_color(*var2, is_background);
@ -331,7 +376,7 @@ rgb_color_t highlight_get_color(highlight_spec_t highlight, bool is_background)
}
}
if (highlight & highlight_modifier_force_underline) {
if (!is_background && highlight.force_underline) {
result.set_underline(true);
}
@ -435,9 +480,9 @@ static size_t color_variable(const wchar_t *in, size_t in_len,
// Our color depends on the next char.
wchar_t next = in[idx + 1];
if (next == L'$' || valid_var_name_char(next)) {
colors[idx] = highlight_spec_operator;
colors[idx] = highlight_role_t::operat;
} else {
colors[idx] = highlight_spec_error;
colors[idx] = highlight_role_t::error;
}
idx++;
dollar_count++;
@ -445,7 +490,7 @@ static size_t color_variable(const wchar_t *in, size_t in_len,
// Handle a sequence of variable characters.
while (valid_var_name_char(in[idx])) {
colors[idx++] = highlight_spec_operator;
colors[idx++] = highlight_role_t::operat;
}
// Handle a slice, up to dollar_count of them. Note that we currently don't do any validation of
@ -456,8 +501,8 @@ static size_t color_variable(const wchar_t *in, size_t in_len,
if (located == 1) {
size_t slice_begin_idx = slice_begin - in, slice_end_idx = slice_end - in;
assert(slice_end_idx > slice_begin_idx);
colors[slice_begin_idx] = highlight_spec_operator;
colors[slice_end_idx] = highlight_spec_operator;
colors[slice_begin_idx] = highlight_role_t::operat;
colors[slice_end_idx] = highlight_role_t::operat;
idx = slice_end_idx + 1;
} else if (located == 0) {
// not a slice
@ -468,7 +513,7 @@ static size_t color_variable(const wchar_t *in, size_t in_len,
// double-quoted string that doesn't happen. As such, color the variable + the slice
// start red. Coloring any more than that looks bad, unless we're willing to try and
// detect where the double-quoted string ends, and I'd rather not do that.
std::fill(colors, colors + idx + 1, (highlight_spec_t)highlight_spec_error);
std::fill(colors, colors + idx + 1, highlight_role_t::error);
break;
}
}
@ -480,14 +525,14 @@ static size_t color_variable(const wchar_t *in, size_t in_len,
static void color_string_internal(const wcstring &buffstr, highlight_spec_t base_color,
std::vector<highlight_spec_t>::iterator colors) {
// Clarify what we expect.
assert((base_color == highlight_spec_param || base_color == highlight_spec_command) &&
assert((base_color == highlight_role_t::param || base_color == highlight_role_t::command) &&
"Unexpected base color");
const size_t buff_len = buffstr.size();
std::fill(colors, colors + buff_len, base_color);
// Hacky support for %self which must be an unquoted literal argument.
if (buffstr == PROCESS_EXPAND_SELF_STR) {
std::fill_n(colors, wcslen(PROCESS_EXPAND_SELF_STR), highlight_spec_operator);
std::fill_n(colors, wcslen(PROCESS_EXPAND_SELF_STR), highlight_role_t::operat);
return;
}
@ -498,7 +543,7 @@ static void color_string_internal(const wcstring &buffstr, highlight_spec_t base
switch (mode) {
case e_unquoted: {
if (c == L'\\') {
int fill_color = highlight_spec_escape; // may be set to highlight_error
auto fill_color = highlight_role_t::escape; // may be set to highlight_error
const size_t backslash_pos = in_pos;
size_t fill_end = backslash_pos;
@ -508,7 +553,7 @@ static void color_string_internal(const wcstring &buffstr, highlight_spec_t base
if (escaped_char == L'\0') {
fill_end = in_pos;
fill_color = highlight_spec_error;
fill_color = highlight_role_t::error;
} else if (wcschr(L"~%", escaped_char)) {
if (in_pos == 1) {
fill_end = in_pos + 1;
@ -571,7 +616,7 @@ static void color_string_internal(const wcstring &buffstr, highlight_spec_t base
fill_end = in_pos;
// It's an error if we exceeded the max value.
if (res > max_val) fill_color = highlight_spec_error;
if (res > max_val) fill_color = highlight_role_t::error;
// Subtract one from in_pos, so that the increment in the loop will move to
// the next character.
@ -584,7 +629,7 @@ static void color_string_internal(const wcstring &buffstr, highlight_spec_t base
switch (c) {
case L'~': {
if (in_pos == 0) {
colors[in_pos] = highlight_spec_operator;
colors[in_pos] = highlight_role_t::operat;
}
break;
}
@ -598,39 +643,39 @@ static void color_string_internal(const wcstring &buffstr, highlight_spec_t base
}
case L'?': {
if (!feature_test(features_t::qmark_noglob)) {
colors[in_pos] = highlight_spec_operator;
colors[in_pos] = highlight_role_t::operat;
}
break;
}
case L'*':
case L'(':
case L')': {
colors[in_pos] = highlight_spec_operator;
colors[in_pos] = highlight_role_t::operat;
break;
}
case L'{': {
colors[in_pos] = highlight_spec_operator;
colors[in_pos] = highlight_role_t::operat;
bracket_count++;
break;
}
case L'}': {
colors[in_pos] = highlight_spec_operator;
colors[in_pos] = highlight_role_t::operat;
bracket_count--;
break;
}
case L',': {
if (bracket_count > 0) {
colors[in_pos] = highlight_spec_operator;
colors[in_pos] = highlight_role_t::operat;
}
break;
}
case L'\'': {
colors[in_pos] = highlight_spec_quote;
colors[in_pos] = highlight_role_t::quote;
mode = e_single_quoted;
break;
}
case L'\"': {
colors[in_pos] = highlight_spec_quote;
colors[in_pos] = highlight_role_t::quote;
mode = e_double_quoted;
break;
}
@ -643,14 +688,14 @@ static void color_string_internal(const wcstring &buffstr, highlight_spec_t base
}
// Mode 1 means single quoted string, i.e 'foo'.
case e_single_quoted: {
colors[in_pos] = highlight_spec_quote;
colors[in_pos] = highlight_role_t::quote;
if (c == L'\\') {
// backslash
if (in_pos + 1 < buff_len) {
const wchar_t escaped_char = buffstr.at(in_pos + 1);
if (escaped_char == L'\\' || escaped_char == L'\'') {
colors[in_pos] = highlight_spec_escape; // backslash
colors[in_pos + 1] = highlight_spec_escape; // escaped char
colors[in_pos] = highlight_role_t::escape; // backslash
colors[in_pos + 1] = highlight_role_t::escape; // escaped char
in_pos += 1; // skip over backslash
}
}
@ -664,7 +709,7 @@ static void color_string_internal(const wcstring &buffstr, highlight_spec_t base
// Slices are colored in advance, past `in_pos`, and we don't want to overwrite
// that.
if (colors[in_pos] == base_color) {
colors[in_pos] = highlight_spec_quote;
colors[in_pos] = highlight_role_t::quote;
}
switch (c) {
case L'"': {
@ -676,8 +721,8 @@ static void color_string_internal(const wcstring &buffstr, highlight_spec_t base
if (in_pos + 1 < buff_len) {
const wchar_t escaped_char = buffstr.at(in_pos + 1);
if (wcschr(L"\\\"\n$", escaped_char)) {
colors[in_pos] = highlight_spec_escape; // backslash
colors[in_pos + 1] = highlight_spec_escape; // escaped char
colors[in_pos] = highlight_role_t::escape; // backslash
colors[in_pos + 1] = highlight_role_t::escape; // escaped char
in_pos += 1; // skip over backslash
}
}
@ -778,7 +823,7 @@ void highlighter_t::color_command(tnode_t<g::tok_string> node) {
// Get an iterator to the colors associated with the argument.
const size_t arg_start = source_range->start;
const color_array_t::iterator colors = color_array.begin() + arg_start;
color_string_internal(cmd_str, highlight_spec_command, colors);
color_string_internal(cmd_str, highlight_role_t::command, colors);
}
// node does not necessarily have type symbol_argument here.
@ -793,7 +838,7 @@ void highlighter_t::color_argument(tnode_t<g::tok_string> node) {
const color_array_t::iterator arg_colors = color_array.begin() + arg_start;
// Color this argument without concern for command substitutions.
color_string_internal(arg_str, highlight_spec_param, arg_colors);
color_string_internal(arg_str, highlight_role_t::param, arg_colors);
// Now do command substitutions.
size_t cmdsub_cursor = 0, cmdsub_start = 0, cmdsub_end = 0;
@ -814,9 +859,9 @@ void highlighter_t::color_argument(tnode_t<g::tok_string> node) {
// Highlight the parens. The open paren must exist; the closed paren may not if it was
// incomplete.
assert(cmdsub_start < arg_str.size());
this->color_array.at(arg_subcmd_start) = highlight_spec_operator;
this->color_array.at(arg_subcmd_start) = highlight_role_t::operat;
if (arg_subcmd_end < this->buff.size())
this->color_array.at(arg_subcmd_end) = highlight_spec_operator;
this->color_array.at(arg_subcmd_end) = highlight_role_t::operat;
// Compute the cursor's position within the cmdsub. We must be past the open paren (hence >)
// but can be at the end of the string or closed paren (hence <=).
@ -886,7 +931,7 @@ void highlighter_t::color_arguments(const std::vector<tnode_t<g::argument>> &arg
string_prefixes_string(param, L"-h");
if (!is_help && this->io_ok &&
!is_potential_cd_path(param, working_directory, vars, PATH_EXPAND_TILDE)) {
this->color_node(arg, highlight_spec_error);
this->color_node(arg, highlight_role_t::error);
}
}
}
@ -905,7 +950,7 @@ void highlighter_t::color_redirection(tnode_t<g::redirection> redirection_node)
redirection_type(redirection_node, this->buff, nullptr, &target);
// We may get a missing redirection type if the redirection is invalid.
auto hl = redirect_type ? highlight_spec_redirection : highlight_spec_error;
auto hl = redirect_type ? highlight_role_t::redirection : highlight_role_t::error;
this->color_node(redir_prim, hl);
// Check if the argument contains a command substitution. If so, highlight it as a param
@ -999,7 +1044,7 @@ void highlighter_t::color_redirection(tnode_t<g::redirection> redirection_node)
}
if (redir_target) {
auto hl = target_is_valid ? highlight_spec_redirection : highlight_spec_error;
auto hl = target_is_valid ? highlight_role_t::redirection : highlight_role_t::error;
this->color_node(redir_target, hl);
}
}
@ -1080,7 +1125,7 @@ const highlighter_t::color_array_t &highlighter_t::highlight() {
if (length == 0) return color_array;
// Start out at zero.
std::fill(this->color_array.begin(), this->color_array.end(), 0);
std::fill(this->color_array.begin(), this->color_array.end(), highlight_spec_t{});
// Walk the node tree.
for (const parse_node_t &node : parse_tree) {
@ -1094,15 +1139,15 @@ const highlighter_t::color_array_t &highlighter_t::highlight() {
case symbol_case_item:
case symbol_decorated_statement:
case symbol_if_statement: {
this->color_children(node, parse_token_type_string, highlight_spec_command);
this->color_children(node, parse_token_type_string, highlight_role_t::command);
break;
}
case symbol_switch_statement: {
tnode_t<g::switch_statement> switchn(&parse_tree, &node);
auto literal_switch = switchn.child<0>();
auto switch_arg = switchn.child<1>();
this->color_node(literal_switch, highlight_spec_command);
this->color_node(switch_arg, highlight_spec_param);
this->color_node(literal_switch, highlight_role_t::command);
this->color_node(switch_arg, highlight_role_t::param);
break;
}
case symbol_for_header: {
@ -1110,8 +1155,8 @@ const highlighter_t::color_array_t &highlighter_t::highlight() {
// Color the 'for' and 'in' as commands.
auto literal_for = fhead.child<0>();
auto literal_in = fhead.child<2>();
this->color_node(literal_for, highlight_spec_command);
this->color_node(literal_in, highlight_spec_command);
this->color_node(literal_for, highlight_role_t::command);
this->color_node(literal_in, highlight_role_t::command);
// Color the variable name as a parameter.
this->color_argument(fhead.child<1>());
@ -1120,22 +1165,22 @@ const highlighter_t::color_array_t &highlighter_t::highlight() {
case parse_token_type_andand:
case parse_token_type_oror:
this->color_node(node, highlight_spec_operator);
this->color_node(node, highlight_role_t::operat);
break;
case symbol_not_statement:
this->color_children(node, parse_token_type_string, highlight_spec_operator);
this->color_children(node, parse_token_type_string, highlight_role_t::operat);
break;
case symbol_job_decorator:
this->color_node(node, highlight_spec_operator);
this->color_node(node, highlight_role_t::operat);
break;
case parse_token_type_pipe:
case parse_token_type_background:
case parse_token_type_end:
case symbol_optional_background: {
this->color_node(node, highlight_spec_statement_terminator);
this->color_node(node, highlight_role_t::statement_terminator);
break;
}
case symbol_plain_statement: {
@ -1166,7 +1211,7 @@ const highlighter_t::color_array_t &highlighter_t::highlight() {
}
}
if (!is_valid_cmd) {
this->color_node(*cmd_node, highlight_spec_error);
this->color_node(*cmd_node, highlight_role_t::error);
} else {
this->color_command(cmd_node);
}
@ -1190,16 +1235,16 @@ const highlighter_t::color_array_t &highlighter_t::highlight() {
break;
}
case symbol_end_command: {
this->color_node(node, highlight_spec_command);
this->color_node(node, highlight_role_t::command);
break;
}
case parse_special_type_parse_error:
case parse_special_type_tokenizer_error: {
this->color_node(node, highlight_spec_error);
this->color_node(node, highlight_role_t::error);
break;
}
case parse_special_type_comment: {
this->color_node(node, highlight_spec_comment);
this->color_node(node, highlight_role_t::comment);
break;
}
default: { break; }
@ -1225,10 +1270,10 @@ const highlighter_t::color_array_t &highlighter_t::highlight() {
node_is_potential_path(buff, node, vars, working_directory)) {
// It is, underline it.
for (size_t i = node.source_start; i < node.source_start + node.source_length; i++) {
// Don't color highlight_spec_error because it looks dorky. For example,
// Don't color highlight_role_t::error because it looks dorky. For example,
// trying to cd into a non-directory would show an underline and also red.
if (highlight_get_primary(this->color_array.at(i)) != highlight_spec_error) {
this->color_array.at(i) |= highlight_modifier_valid_path;
if (this->color_array.at(i).foreground != highlight_role_t::error) {
this->color_array.at(i).valid_path = true;
}
}
}
@ -1295,10 +1340,8 @@ static void highlight_universal_internal(const wcstring &buffstr,
pos1 = lst.back();
pos2 = str - buff;
if (pos1 == pos || pos2 == pos) {
color.at(pos1) |=
highlight_make_background(highlight_spec_match);
color.at(pos2) |=
highlight_make_background(highlight_spec_match);
color.at(pos1).background = highlight_role_t::match;
color.at(pos2).background = highlight_role_t::match;
match_found = true;
}
prev_q = *str == L'\"' ? L'\'' : L'\"';
@ -1318,7 +1361,7 @@ static void highlight_universal_internal(const wcstring &buffstr,
str++;
}
if (!match_found) color.at(pos) = highlight_make_background(highlight_spec_error);
if (!match_found) color.at(pos).background = highlight_role_t::error;
}
// Highlight matching parenthesis.
@ -1335,14 +1378,15 @@ static void highlight_universal_internal(const wcstring &buffstr,
if (test_char == dec_char) level--;
if (level == 0) {
long pos2 = i;
color.at(pos) |= highlight_spec_match << 16;
color.at(pos2) |= highlight_spec_match << 16;
color.at(pos).background = highlight_role_t::match;
color.at(pos2).background = highlight_role_t::match;
match_found = true;
break;
}
}
if (!match_found) color[pos] = highlight_make_background(highlight_spec_error);
if (!match_found)
color.at(pos) = highlight_spec_t::make_background(highlight_role_t::error);
}
}
}
@ -1352,6 +1396,6 @@ void highlight_universal(const wcstring &buff, std::vector<highlight_spec_t> &co
UNUSED(error);
UNUSED(vars);
assert(buff.size() == color.size());
std::fill(color.begin(), color.end(), 0);
std::fill(color.begin(), color.end(), highlight_spec_t{});
highlight_universal_internal(buff, color, pos);
}

View File

@ -11,66 +11,64 @@
#include "common.h"
#include "env.h"
// Internally, we specify highlight colors using a set of bits. Each highlight_spec is a 32 bit
// uint. We divide this into low 16 (foreground) and high 16 (background). Each half we further
// subdivide into low 8 (primary) and high 8 (modifiers). The primary is not a bitmask; specify
// exactly one. The modifiers are a bitmask; specify any number.
enum {
// The following values are mutually exclusive; specify at most one.
highlight_spec_normal = 0, // normal text
highlight_spec_error, // error
highlight_spec_command, // command
highlight_spec_statement_terminator, // process separator
highlight_spec_param, // command parameter (argument)
highlight_spec_comment, // comment
highlight_spec_match, // matching parenthesis, etc.
highlight_spec_search_match, // search match
highlight_spec_operator, // operator
highlight_spec_escape, // escape sequences
highlight_spec_quote, // quoted string
highlight_spec_redirection, // redirection
highlight_spec_autosuggestion, // autosuggestion
highlight_spec_selection,
/// Describes the role of a span of text.
enum class highlight_role_t : uint8_t {
normal = 0, // normal text
error, // error
command, // command
statement_terminator, // process separator
param, // command parameter (argument)
comment, // comment
match, // matching parenthesis, etc.
search_match, // search match
operat, // operator
escape, // escape sequences
quote, // quoted string
redirection, // redirection
autosuggestion, // autosuggestion
selection,
// Pager support.
// NOTE: pager.cpp relies on these being in this order.
highlight_spec_pager_progress,
highlight_spec_pager_background,
highlight_spec_pager_prefix,
highlight_spec_pager_completion,
highlight_spec_pager_description,
highlight_spec_pager_secondary_background,
highlight_spec_pager_secondary_prefix,
highlight_spec_pager_secondary_completion,
highlight_spec_pager_secondary_description,
highlight_spec_pager_selected_background,
highlight_spec_pager_selected_prefix,
highlight_spec_pager_selected_completion,
highlight_spec_pager_selected_description,
// Used to double check a data structure in highlight.cpp
HIGHLIGHT_SPEC_MAX,
HIGHLIGHT_SPEC_PRIMARY_MASK = 0xFF,
// The following values are modifiers.
highlight_modifier_valid_path = 0x100,
highlight_modifier_force_underline = 0x200,
/* Very special value */
highlight_spec_invalid = 0xFFFF
pager_progress,
pager_background,
pager_prefix,
pager_completion,
pager_description,
pager_secondary_background,
pager_secondary_prefix,
pager_secondary_completion,
pager_secondary_description,
pager_selected_background,
pager_selected_prefix,
pager_selected_completion,
pager_selected_description,
};
typedef uint32_t highlight_spec_t;
inline highlight_spec_t highlight_get_primary(highlight_spec_t val) {
return val & HIGHLIGHT_SPEC_PRIMARY_MASK;
}
/// Simply value type describing how a character should be highlighted..
struct highlight_spec_t {
highlight_role_t foreground{highlight_role_t::normal};
highlight_role_t background{highlight_role_t::normal};
bool valid_path{false};
bool force_underline{false};
inline highlight_spec_t highlight_make_background(highlight_spec_t val) {
assert(val >> 16 ==
0); // should have nothing in upper bits, otherwise this is already a background
return val << 16;
}
highlight_spec_t() = default;
/* implicit */ highlight_spec_t(highlight_role_t fg,
highlight_role_t bg = highlight_role_t::normal)
: foreground(fg), background(bg) {}
bool operator==(const highlight_spec_t &rhs) const {
return foreground == rhs.foreground && background == rhs.background &&
valid_path == rhs.valid_path && force_underline == rhs.force_underline;
}
bool operator!=(const highlight_spec_t &rhs) const { return !(*this == rhs); }
static highlight_spec_t make_background(highlight_role_t bg_role) {
return highlight_spec_t{highlight_role_t::normal, bg_role};
}
};
class history_item_t;
@ -104,14 +102,8 @@ void highlight_shell_no_io(const wcstring &buffstr, std::vector<highlight_spec_t
void highlight_universal(const wcstring &buffstr, std::vector<highlight_spec_t> &color, size_t pos,
wcstring_list_t *error, const environment_t &vars);
/// Translate from HIGHLIGHT_* to FISH_COLOR_* according to environment variables. Defaults to
/// FISH_COLOR_NORMAL.
///
/// Example:
///
/// If the environment variable FISH_FISH_COLOR_ERROR is set to 'red', a call to
/// highlight_get_color( highlight_error) will return FISH_COLOR_RED.
rgb_color_t highlight_get_color(highlight_spec_t highlight, bool is_background);
/// \return an RGB color for a given highlight spec.
rgb_color_t highlight_get_color(const highlight_spec_t &highlight, bool is_background);
/// Given a command 'str' from the history, try to determine whether we ought to suggest it by
/// specially recognizing the command. Returns true if we validated the command. If so, returns by

View File

@ -7,6 +7,7 @@
#include <algorithm>
#include <numeric>
#include <type_traits>
#include <unordered_map>
#include <vector>
@ -137,20 +138,25 @@ line_t pager_t::completion_print_item(const wcstring &prefix, const comp_t *c, s
assert(comp_width <= width);
}
int offset = selected
? (highlight_spec_pager_selected_background - highlight_spec_pager_background)
: (secondary
? (highlight_spec_pager_secondary_background - highlight_spec_pager_background)
: 0);
highlight_spec_t bg_color = highlight_spec_pager_background + offset;
highlight_spec_t prefix_fg = highlight_spec_pager_prefix + offset;
highlight_spec_t comp_fg = highlight_spec_pager_completion + offset;
highlight_spec_t desc_fg = highlight_spec_pager_description + offset;
auto modify_role = [=](highlight_role_t role) -> highlight_role_t {
using uint_t = typename std::underlying_type<highlight_role_t>::type;
uint_t base = static_cast<uint_t>(role);
if (selected) {
base += static_cast<uint_t>(highlight_role_t::pager_selected_background) -
static_cast<uint_t>(highlight_role_t::pager_background);
} else if (secondary) {
base += static_cast<uint_t>(highlight_role_t::pager_secondary_background) -
static_cast<uint_t>(highlight_role_t::pager_background);
}
return static_cast<highlight_role_t>(base);
};
highlight_role_t bg_role = modify_role(highlight_role_t::pager_background);
highlight_spec_t bg = {highlight_role_t::normal, bg_role};
highlight_spec_t prefix_col = {modify_role(highlight_role_t::pager_prefix), bg_role};
highlight_spec_t comp_col = {modify_role(highlight_role_t::pager_completion), bg_role};
highlight_spec_t desc_col = {modify_role(highlight_role_t::pager_description), bg_role};
auto bg = highlight_make_background(bg_color);
auto prefix_col = prefix_fg | bg;
auto comp_col = comp_fg | bg;
auto desc_col = desc_fg | bg;
// Print the completion part
size_t comp_remaining = comp_width;
for (size_t i = 0; i < c->comp.size(); i++) {
@ -179,7 +185,7 @@ line_t pager_t::completion_print_item(const wcstring &prefix, const comp_t *c, s
}
assert(desc_remaining >= 2);
auto paren_col = highlight_spec_pager_completion | bg;
highlight_spec_t paren_col = {highlight_role_t::pager_completion, bg_role};
desc_remaining -= print_max(L"(", paren_col, 1, false, &line_data);
desc_remaining -= print_max(c->desc, desc_col, desc_remaining - 1, false, &line_data);
desc_remaining -= print_max(L")", paren_col, 1, false, &line_data);
@ -226,7 +232,7 @@ void pager_t::completion_print(size_t cols, const size_t *width_by_column, size_
// If there's more to come, append two spaces.
if (col + 1 < cols) {
line.append(PAGER_SPACER_STRING, 0);
line.append(PAGER_SPACER_STRING, highlight_spec_t{});
}
// Append this to the real line.
@ -503,8 +509,8 @@ bool pager_t::completion_try_print(size_t cols, const wcstring &prefix, const co
if (!progress_text.empty()) {
line_t &line = rendering->screen_data.add_line();
highlight_spec_t spec = highlight_spec_pager_progress |
highlight_make_background(highlight_spec_pager_progress);
highlight_spec_t spec = {highlight_role_t::pager_progress,
highlight_role_t::pager_progress};
print_max(progress_text, spec, term_width, true /* has_more */, &line);
}
@ -521,11 +527,14 @@ bool pager_t::completion_try_print(size_t cols, const wcstring &prefix, const co
line_t *search_field = &rendering->screen_data.insert_line_at_index(0);
// We limit the width to term_width - 1.
highlight_spec_t underline{};
underline.force_underline = true;
size_t search_field_remaining = term_width - 1;
search_field_remaining -= print_max(SEARCH_FIELD_PROMPT, highlight_spec_normal,
search_field_remaining, false, search_field);
search_field_remaining -= print_max(search_field_text, highlight_modifier_force_underline,
search_field_remaining -= print_max(SEARCH_FIELD_PROMPT, highlight_role_t::normal,
search_field_remaining, false, search_field);
search_field_remaining -=
print_max(search_field_text, underline, search_field_remaining, false, search_field);
return true;
}

View File

@ -617,10 +617,10 @@ void reader_data_t::repaint() {
if (len < 1) len = 1;
std::vector<highlight_spec_t> colors = this->colors;
colors.resize(len, highlight_spec_autosuggestion);
colors.resize(len, highlight_role_t::autosuggestion);
if (sel_active) {
highlight_spec_t selection_color = highlight_make_background(highlight_spec_selection);
highlight_spec_t selection_color = {highlight_role_t::normal, highlight_role_t::selection};
for (size_t i = sel_start_pos; i < std::min(len, sel_stop_pos); i++) {
colors[i] = selection_color;
}
@ -1433,7 +1433,7 @@ void reader_data_t::flash() {
struct timespec pollint;
editable_line_t *el = &command_line;
for (size_t i = 0; i < el->position; i++) {
colors.at(i) = highlight_spec_search_match << 16;
colors.at(i) = highlight_spec_t::make_background(highlight_role_t::search_match);
}
repaint();
@ -2025,7 +2025,7 @@ void reader_data_t::highlight_search() {
if (match_pos != wcstring::npos) {
size_t end = match_pos + needle.size();
for (size_t i = match_pos; i < end; i++) {
colors.at(i) |= (highlight_spec_search_match << 16);
colors.at(i).background = highlight_role_t::search_match;
}
}
}
@ -2059,7 +2059,7 @@ static std::function<highlight_result_t(void)> get_highlight_performer(
return {};
}
s_thread_generation = generation_count;
std::vector<highlight_spec_t> colors(text.size(), 0);
std::vector<highlight_spec_t> colors(text.size(), highlight_spec_t{});
highlight_func(text, colors, match_highlight_pos, NULL /* error */, vars);
return {std::move(colors), text};
};

View File

@ -376,7 +376,8 @@ static void s_check_status(screen_t *s) {
/// Appends a character to the end of the line that the output cursor is on. This function
/// automatically handles linebreaks and lines longer than the screen width.
static void s_desired_append_char(screen_t *s, wchar_t b, int c, int indent, size_t prompt_width) {
static void s_desired_append_char(screen_t *s, wchar_t b, highlight_spec_t c, int indent,
size_t prompt_width) {
int line_no = s->desired.cursor.y;
if (b == L'\n') {
@ -386,7 +387,7 @@ static void s_desired_append_char(screen_t *s, wchar_t b, int c, int indent, siz
s->desired.cursor.y++;
s->desired.cursor.x = 0;
for (size_t i = 0; i < prompt_width + indent * INDENT_STEP; i++) {
s_desired_append_char(s, L' ', 0, indent, prompt_width);
s_desired_append_char(s, L' ', highlight_spec_t{}, indent, prompt_width);
}
} else if (b == L'\r') {
line_t &current = s->desired.line(line_no);
@ -512,10 +513,7 @@ static void s_move(screen_t *s, int new_x, int new_y) {
/// Set the pen color for the terminal.
static void s_set_color(screen_t *s, const environment_t &vars, highlight_spec_t c) {
UNUSED(s);
unsigned int uc = (unsigned int)c;
s->outp().set_color(highlight_get_color(uc & 0xfff, false),
highlight_get_color((uc >> 16) & 0xffff, true));
s->outp().set_color(highlight_get_color(c, false), highlight_get_color(c, true));
}
/// Convert a wide character to a multibyte string and append it to the buffer.
@ -723,7 +721,7 @@ static void s_update(screen_t *scr, const wcstring &left_prompt, const wcstring
clear_remainder = prev_width > current_width;
}
if (clear_remainder && clr_eol) {
s_set_color(scr, vars, 0xffffffff);
s_set_color(scr, vars, highlight_spec_t{});
s_move(scr, current_width, (int)i);
s_write_mbs(scr, clr_eol);
}
@ -731,7 +729,7 @@ static void s_update(screen_t *scr, const wcstring &left_prompt, const wcstring
// Output any rprompt if this is the first line.
if (i == 0 && right_prompt_width > 0) { //!OCLINT(Use early exit/continue)
s_move(scr, (int)(screen_width - right_prompt_width), (int)i);
s_set_color(scr, vars, 0xffffffff);
s_set_color(scr, vars, highlight_spec_t{});
s_write_str(scr, right_prompt.c_str());
scr->actual.cursor.x += right_prompt_width;
@ -751,7 +749,7 @@ static void s_update(screen_t *scr, const wcstring &left_prompt, const wcstring
// Clear remaining lines (if any) if we haven't cleared the screen.
if (!has_cleared_screen && scr->desired.line_count() < lines_with_stuff && clr_eol) {
s_set_color(scr, vars, 0xffffffff);
s_set_color(scr, vars, highlight_spec_t{});
for (size_t i = scr->desired.line_count(); i < lines_with_stuff; i++) {
s_move(scr, 0, (int)i);
s_write_mbs(scr, clr_eol);
@ -759,7 +757,7 @@ static void s_update(screen_t *scr, const wcstring &left_prompt, const wcstring
}
s_move(scr, scr->desired.cursor.x, scr->desired.cursor.y);
s_set_color(scr, vars, 0xffffffff);
s_set_color(scr, vars, highlight_spec_t{});
// We have now synced our actual screen against our desired screen. Note that this is a big
// assignment!
@ -999,13 +997,13 @@ void s_write(screen_t *s, const wcstring &left_prompt, const wcstring &right_pro
// Append spaces for the left prompt.
for (size_t i = 0; i < layout.left_prompt_space; i++) {
s_desired_append_char(s, L' ', 0, 0, layout.left_prompt_space);
s_desired_append_char(s, L' ', highlight_spec_t{}, 0, layout.left_prompt_space);
}
// If overflowing, give the prompt its own line to improve the situation.
size_t first_line_prompt_space = layout.left_prompt_space;
if (layout.prompts_get_own_line) {
s_desired_append_char(s, L'\n', 0, 0, 0);
s_desired_append_char(s, L'\n', highlight_spec_t{}, 0, 0);
first_line_prompt_space = 0;
}