Add fish_emoji_width variable to control computed emoji width

This is part of an effort to improve fish's Unicode handling. This commit
attempts to grapple with the fact that, certain characters (principally
emoji) were considered to have a wcwidth of 1 in Unicode 8, but a width of
2 in Unicode 9.

The system wcwidth() here cannot be trusted; terminal emulators do not
respect it. iTerm2 even allows this to be set in preferences.

This commit introduces a new function is_width_2_in_Uni9_but_1_in_Uni8() to
detect characters of version-ambiguous width. For these characters, it
returns a width guessed based on the value of TERM_PROGRAM and
TERM_VERSION, defaulting to 1. This value can be overridden by setting the
value of a new variable fish_emoji_width (presumably either to 1 or 2).

Fixes #4539, #2652.
This commit is contained in:
ridiculousfish
2018-02-25 17:43:29 -08:00
parent 7bd4af51a1
commit 5282d3e711
4 changed files with 92 additions and 0 deletions

View File

@@ -571,6 +571,30 @@ static void init_path_vars() {
}
}
/// Update the value of g_guessed_fish_emoji_width
static void guess_emoji_width() {
wcstring term;
if (auto term_var = env_get(L"TERM_PROGRAM")) {
term = term_var->as_string();
}
double version = 0;
if (auto version_var = env_get(L"TERM_PROGRAM_VERSION")) {
std::string narrow_version = wcs2string(version_var->as_string());
version = strtod(narrow_version.c_str(), NULL);
}
// iTerm2 defaults to Unicode 8 sizes.
// See https://gitlab.com/gnachman/iterm2/wikis/unicodeversionswitching
if (term == L"Apple_Terminal" && version >= 400) {
// Apple Terminal on High Sierra
g_guessed_fish_emoji_width = 2;
} else {
g_guessed_fish_emoji_width = 1;
}
}
/// Initialize the curses subsystem.
static void init_curses() {
for (const auto &var_name : curses_variables) {
@@ -798,6 +822,14 @@ static void handle_escape_delay_change(const wcstring &op, const wcstring &var_n
update_wait_on_escape_ms();
}
static void handle_change_emoji_width(const wcstring &op, const wcstring &var_name) {
int new_width = 0;
if (auto width_str = env_get(L"fish_emoji_width")) {
new_width = fish_wcstol(width_str->as_string().c_str());
}
g_fish_emoji_width = std::max(0, new_width);
}
static void handle_term_size_change(const wcstring &op, const wcstring &var_name) {
UNUSED(op);
UNUSED(var_name);
@@ -847,6 +879,7 @@ static void handle_locale_change(const wcstring &op, const wcstring &var_name) {
static void handle_curses_change(const wcstring &op, const wcstring &var_name) {
UNUSED(op);
UNUSED(var_name);
guess_emoji_width();
init_curses();
}
@@ -868,6 +901,7 @@ static void setup_var_dispatch_table() {
var_dispatch_table.emplace(L"fish_term256", handle_fish_term_change);
var_dispatch_table.emplace(L"fish_term24bit", handle_fish_term_change);
var_dispatch_table.emplace(L"fish_escape_delay_ms", handle_escape_delay_change);
var_dispatch_table.emplace(L"fish_emoji_width", handle_change_emoji_width);
var_dispatch_table.emplace(L"LINES", handle_term_size_change);
var_dispatch_table.emplace(L"COLUMNS", handle_term_size_change);
var_dispatch_table.emplace(L"fish_complete_path", handle_complete_path_change);
@@ -925,6 +959,7 @@ void env_init(const struct config_paths_t *paths /* or NULL */) {
init_curses();
init_input();
init_path_vars();
guess_emoji_width();
// Set up the USER and PATH variables
setup_path();