Set of changes to improve Unicode support with respect to combining characters.

Should address https://github.com/fish-shell/fish-shell/issues/155
This commit is contained in:
ridiculousfish 2012-07-15 10:45:18 -07:00
parent b1281c3fb9
commit ea1bfd715e
8 changed files with 403 additions and 111 deletions

View file

@ -511,18 +511,7 @@ int wcsvarchr( wchar_t chr )
*/ */
int my_wcswidth( const wchar_t *c ) int my_wcswidth( const wchar_t *c )
{ {
int res=0; return fish_wcswidth(c, wcslen(c));
while( *c )
{
int w = wcwidth( *c++ );
if( w < 0 )
w = 1;
if( w > 2 )
w=1;
res += w;
}
return res;
} }
wchar_t *quote_end( const wchar_t *pos ) wchar_t *quote_end( const wchar_t *pos )
@ -822,13 +811,13 @@ void write_screen( const wcstring &msg, wcstring &buff )
Check is token is wider than one line. Check is token is wider than one line.
If so we mark it as an overflow and break the token. If so we mark it as an overflow and break the token.
*/ */
if((tok_width + wcwidth(*pos)) > (screen_width-1)) if((tok_width + fish_wcwidth(*pos)) > (screen_width-1))
{ {
overflow = 1; overflow = 1;
break; break;
} }
tok_width += wcwidth( *pos ); tok_width += fish_wcwidth( *pos );
pos++; pos++;
} }

View file

@ -1192,3 +1192,258 @@ double nan(char *tagp)
} }
#endif #endif
/* Big hack to use our versions of wcswidth where we know them to be broken, like on OS X */
#ifndef HAVE_BROKEN_WCWIDTH
#if __APPLE__
#define HAVE_BROKEN_WCWIDTH 1
#else
#define HAVE_BROKEN_WCWIDTH 0
#endif
#endif
#if ! HAVE_BROKEN_WCWIDTH
int fish_wcwidth(wchar_t wc)
{
return wcwidth(wc);
}
int fish_wcswidth(const wchar_t *str, size_t n)
{
return wcswidth(str, n);
}
#else
static int mk_wcwidth(wchar_t wc);
static int mk_wcswidth(const wchar_t *pwcs, size_t n);
int fish_wcwidth(wchar_t wc)
{
return mk_wcwidth(wc);
}
int fish_wcswidth(const wchar_t *str, size_t n)
{
return mk_wcswidth(str, n);
}
/*
* This is an implementation of wcwidth() and wcswidth() (defined in
* IEEE Std 1002.1-2001) for Unicode.
*
* http://www.opengroup.org/onlinepubs/007904975/functions/wcwidth.html
* http://www.opengroup.org/onlinepubs/007904975/functions/wcswidth.html
*
* In fixed-width output devices, Latin characters all occupy a single
* "cell" position of equal width, whereas ideographic CJK characters
* occupy two such cells. Interoperability between terminal-line
* applications and (teletype-style) character terminals using the
* UTF-8 encoding requires agreement on which character should advance
* the cursor by how many cell positions. No established formal
* standards exist at present on which Unicode character shall occupy
* how many cell positions on character terminals. These routines are
* a first attempt of defining such behavior based on simple rules
* applied to data provided by the Unicode Consortium.
*
* For some graphical characters, the Unicode standard explicitly
* defines a character-cell width via the definition of the East Asian
* FullWidth (F), Wide (W), Half-width (H), and Narrow (Na) classes.
* In all these cases, there is no ambiguity about which width a
* terminal shall use. For characters in the East Asian Ambiguous (A)
* class, the width choice depends purely on a preference of backward
* compatibility with either historic CJK or Western practice.
* Choosing single-width for these characters is easy to justify as
* the appropriate long-term solution, as the CJK practice of
* displaying these characters as double-width comes from historic
* implementation simplicity (8-bit encoded characters were displayed
* single-width and 16-bit ones double-width, even for Greek,
* Cyrillic, etc.) and not any typographic considerations.
*
* Much less clear is the choice of width for the Not East Asian
* (Neutral) class. Existing practice does not dictate a width for any
* of these characters. It would nevertheless make sense
* typographically to allocate two character cells to characters such
* as for instance EM SPACE or VOLUME INTEGRAL, which cannot be
* represented adequately with a single-width glyph. The following
* routines at present merely assign a single-cell width to all
* neutral characters, in the interest of simplicity. This is not
* entirely satisfactory and should be reconsidered before
* establishing a formal standard in this area. At the moment, the
* decision which Not East Asian (Neutral) characters should be
* represented by double-width glyphs cannot yet be answered by
* applying a simple rule from the Unicode database content. Setting
* up a proper standard for the behavior of UTF-8 character terminals
* will require a careful analysis not only of each Unicode character,
* but also of each presentation form, something the author of these
* routines has avoided to do so far.
*
* http://www.unicode.org/unicode/reports/tr11/
*
* Markus Kuhn -- 2007-05-26 (Unicode 5.0)
*
* Permission to use, copy, modify, and distribute this software
* for any purpose and without fee is hereby granted. The author
* disclaims all warranties with regard to this software.
*
* Latest version: http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c
*/
#include <wchar.h>
struct interval {
int first;
int last;
};
/* auxiliary function for binary search in interval table */
static int bisearch(wchar_t ucs, const struct interval *table, int max) {
int min = 0;
int mid;
if (ucs < table[0].first || ucs > table[max].last)
return 0;
while (max >= min) {
mid = (min + max) / 2;
if (ucs > table[mid].last)
min = mid + 1;
else if (ucs < table[mid].first)
max = mid - 1;
else
return 1;
}
return 0;
}
/* The following two functions define the column width of an ISO 10646
* character as follows:
*
* - The null character (U+0000) has a column width of 0.
*
* - Other C0/C1 control characters and DEL will lead to a return
* value of -1.
*
* - Non-spacing and enclosing combining characters (general
* category code Mn or Me in the Unicode database) have a
* column width of 0.
*
* - SOFT HYPHEN (U+00AD) has a column width of 1.
*
* - Other format characters (general category code Cf in the Unicode
* database) and ZERO WIDTH SPACE (U+200B) have a column width of 0.
*
* - Hangul Jamo medial vowels and final consonants (U+1160-U+11FF)
* have a column width of 0.
*
* - Spacing characters in the East Asian Wide (W) or East Asian
* Full-width (F) category as defined in Unicode Technical
* Report #11 have a column width of 2.
*
* - All remaining characters (including all printable
* ISO 8859-1 and WGL4 characters, Unicode control characters,
* etc.) have a column width of 1.
*
* This implementation assumes that wchar_t characters are encoded
* in ISO 10646.
*/
static int mk_wcwidth(wchar_t ucs)
{
/* sorted list of non-overlapping intervals of non-spacing characters */
/* generated by "uniset +cat=Me +cat=Mn +cat=Cf -00AD +1160-11FF +200B c" */
static const struct interval combining[] = {
{ 0x0300, 0x036F }, { 0x0483, 0x0486 }, { 0x0488, 0x0489 },
{ 0x0591, 0x05BD }, { 0x05BF, 0x05BF }, { 0x05C1, 0x05C2 },
{ 0x05C4, 0x05C5 }, { 0x05C7, 0x05C7 }, { 0x0600, 0x0603 },
{ 0x0610, 0x0615 }, { 0x064B, 0x065E }, { 0x0670, 0x0670 },
{ 0x06D6, 0x06E4 }, { 0x06E7, 0x06E8 }, { 0x06EA, 0x06ED },
{ 0x070F, 0x070F }, { 0x0711, 0x0711 }, { 0x0730, 0x074A },
{ 0x07A6, 0x07B0 }, { 0x07EB, 0x07F3 }, { 0x0901, 0x0902 },
{ 0x093C, 0x093C }, { 0x0941, 0x0948 }, { 0x094D, 0x094D },
{ 0x0951, 0x0954 }, { 0x0962, 0x0963 }, { 0x0981, 0x0981 },
{ 0x09BC, 0x09BC }, { 0x09C1, 0x09C4 }, { 0x09CD, 0x09CD },
{ 0x09E2, 0x09E3 }, { 0x0A01, 0x0A02 }, { 0x0A3C, 0x0A3C },
{ 0x0A41, 0x0A42 }, { 0x0A47, 0x0A48 }, { 0x0A4B, 0x0A4D },
{ 0x0A70, 0x0A71 }, { 0x0A81, 0x0A82 }, { 0x0ABC, 0x0ABC },
{ 0x0AC1, 0x0AC5 }, { 0x0AC7, 0x0AC8 }, { 0x0ACD, 0x0ACD },
{ 0x0AE2, 0x0AE3 }, { 0x0B01, 0x0B01 }, { 0x0B3C, 0x0B3C },
{ 0x0B3F, 0x0B3F }, { 0x0B41, 0x0B43 }, { 0x0B4D, 0x0B4D },
{ 0x0B56, 0x0B56 }, { 0x0B82, 0x0B82 }, { 0x0BC0, 0x0BC0 },
{ 0x0BCD, 0x0BCD }, { 0x0C3E, 0x0C40 }, { 0x0C46, 0x0C48 },
{ 0x0C4A, 0x0C4D }, { 0x0C55, 0x0C56 }, { 0x0CBC, 0x0CBC },
{ 0x0CBF, 0x0CBF }, { 0x0CC6, 0x0CC6 }, { 0x0CCC, 0x0CCD },
{ 0x0CE2, 0x0CE3 }, { 0x0D41, 0x0D43 }, { 0x0D4D, 0x0D4D },
{ 0x0DCA, 0x0DCA }, { 0x0DD2, 0x0DD4 }, { 0x0DD6, 0x0DD6 },
{ 0x0E31, 0x0E31 }, { 0x0E34, 0x0E3A }, { 0x0E47, 0x0E4E },
{ 0x0EB1, 0x0EB1 }, { 0x0EB4, 0x0EB9 }, { 0x0EBB, 0x0EBC },
{ 0x0EC8, 0x0ECD }, { 0x0F18, 0x0F19 }, { 0x0F35, 0x0F35 },
{ 0x0F37, 0x0F37 }, { 0x0F39, 0x0F39 }, { 0x0F71, 0x0F7E },
{ 0x0F80, 0x0F84 }, { 0x0F86, 0x0F87 }, { 0x0F90, 0x0F97 },
{ 0x0F99, 0x0FBC }, { 0x0FC6, 0x0FC6 }, { 0x102D, 0x1030 },
{ 0x1032, 0x1032 }, { 0x1036, 0x1037 }, { 0x1039, 0x1039 },
{ 0x1058, 0x1059 }, { 0x1160, 0x11FF }, { 0x135F, 0x135F },
{ 0x1712, 0x1714 }, { 0x1732, 0x1734 }, { 0x1752, 0x1753 },
{ 0x1772, 0x1773 }, { 0x17B4, 0x17B5 }, { 0x17B7, 0x17BD },
{ 0x17C6, 0x17C6 }, { 0x17C9, 0x17D3 }, { 0x17DD, 0x17DD },
{ 0x180B, 0x180D }, { 0x18A9, 0x18A9 }, { 0x1920, 0x1922 },
{ 0x1927, 0x1928 }, { 0x1932, 0x1932 }, { 0x1939, 0x193B },
{ 0x1A17, 0x1A18 }, { 0x1B00, 0x1B03 }, { 0x1B34, 0x1B34 },
{ 0x1B36, 0x1B3A }, { 0x1B3C, 0x1B3C }, { 0x1B42, 0x1B42 },
{ 0x1B6B, 0x1B73 }, { 0x1DC0, 0x1DCA }, { 0x1DFE, 0x1DFF },
{ 0x200B, 0x200F }, { 0x202A, 0x202E }, { 0x2060, 0x2063 },
{ 0x206A, 0x206F }, { 0x20D0, 0x20EF }, { 0x302A, 0x302F },
{ 0x3099, 0x309A }, { 0xA806, 0xA806 }, { 0xA80B, 0xA80B },
{ 0xA825, 0xA826 }, { 0xFB1E, 0xFB1E }, { 0xFE00, 0xFE0F },
{ 0xFE20, 0xFE23 }, { 0xFEFF, 0xFEFF }, { 0xFFF9, 0xFFFB },
{ 0x10A01, 0x10A03 }, { 0x10A05, 0x10A06 }, { 0x10A0C, 0x10A0F },
{ 0x10A38, 0x10A3A }, { 0x10A3F, 0x10A3F }, { 0x1D167, 0x1D169 },
{ 0x1D173, 0x1D182 }, { 0x1D185, 0x1D18B }, { 0x1D1AA, 0x1D1AD },
{ 0x1D242, 0x1D244 }, { 0xE0001, 0xE0001 }, { 0xE0020, 0xE007F },
{ 0xE0100, 0xE01EF }
};
/* test for 8-bit control characters */
if (ucs == 0)
return 0;
if (ucs < 32 || (ucs >= 0x7f && ucs < 0xa0))
return -1;
/* binary search in table of non-spacing characters */
if (bisearch(ucs, combining,
sizeof(combining) / sizeof(struct interval) - 1))
return 0;
/* if we arrive here, ucs is not a combining or C0/C1 control character */
return 1 +
(ucs >= 0x1100 &&
(ucs <= 0x115f || /* Hangul Jamo init. consonants */
ucs == 0x2329 || ucs == 0x232a ||
(ucs >= 0x2e80 && ucs <= 0xa4cf &&
ucs != 0x303f) || /* CJK ... Yi */
(ucs >= 0xac00 && ucs <= 0xd7a3) || /* Hangul Syllables */
(ucs >= 0xf900 && ucs <= 0xfaff) || /* CJK Compatibility Ideographs */
(ucs >= 0xfe10 && ucs <= 0xfe19) || /* Vertical forms */
(ucs >= 0xfe30 && ucs <= 0xfe6f) || /* CJK Compatibility Forms */
(ucs >= 0xff00 && ucs <= 0xff60) || /* Fullwidth Forms */
(ucs >= 0xffe0 && ucs <= 0xffe6) ||
(ucs >= 0x20000 && ucs <= 0x2fffd) ||
(ucs >= 0x30000 && ucs <= 0x3fffd)));
}
static int mk_wcswidth(const wchar_t *pwcs, size_t n)
{
int w, width = 0;
for (;*pwcs && n-- > 0; pwcs++)
if ((w = mk_wcwidth(*pwcs)) < 0)
return -1;
else
width += w;
return width;
}
#endif // HAVE_BROKEN_WCWIDTH

View file

@ -12,6 +12,10 @@
#include <sys/types.h> #include <sys/types.h>
#include <signal.h> #include <signal.h>
/** fish's internal versions of wcwidth and wcswidth, which can use an internal implementation if the system one is busted. */
int fish_wcwidth(wchar_t wc);
int fish_wcswidth(const wchar_t *str, size_t n);
#ifndef WCHAR_MAX #ifndef WCHAR_MAX
/** /**
This _should_ be defined by wchar.h, but e.g. OpenBSD doesn't. This _should_ be defined by wchar.h, but e.g. OpenBSD doesn't.

View file

@ -507,8 +507,8 @@ void writestr_ellipsis( const wchar_t *str, int max_width )
while( *str != 0 ) while( *str != 0 )
{ {
int w = wcwidth( *str ); int w = fish_wcwidth( *str );
if( written+w+wcwidth( ellipsis_char )>max_width ) if( written+w+fish_wcwidth( ellipsis_char )>max_width )
{ {
break; break;
} }
@ -516,7 +516,7 @@ void writestr_ellipsis( const wchar_t *str, int max_width )
writech( *(str++) ); writech( *(str++) );
} }
written += wcwidth( ellipsis_char ); written += fish_wcwidth( ellipsis_char );
writech( ellipsis_char ); writech( ellipsis_char );
while( written < max_width ) while( written < max_width )
@ -541,13 +541,13 @@ int write_escaped_str( const wchar_t *str, int max_len )
if( max_len && (max_len < len)) if( max_len && (max_len < len))
{ {
for( i=0; (written+wcwidth(out[i]))<=(max_len-1); i++ ) for( i=0; (written+fish_wcwidth(out[i]))<=(max_len-1); i++ )
{ {
writech( out[i] ); writech( out[i] );
written += wcwidth( out[i] ); written += fish_wcwidth( out[i] );
} }
writech( ellipsis_char ); writech( ellipsis_char );
written += wcwidth( ellipsis_char ); written += fish_wcwidth( ellipsis_char );
for( i=written; i<max_len; i++ ) for( i=written; i<max_len; i++ )
{ {

View file

@ -1080,7 +1080,7 @@ static int printed_width( const wchar_t *str, int len )
} }
else else
{ {
res += wcwidth( str[i] ); res += fish_wcwidth( str[i] );
} }
} }
return res; return res;

View file

@ -721,9 +721,14 @@ static void remove_backward()
if( data->buff_pos <= 0 ) if( data->buff_pos <= 0 )
return; return;
data->command_line.erase(data->buff_pos-1, 1); /* Fake composed character sequences by continuning to delete until we delete a character of width at least 1. */
data->buff_pos--; int width;
do {
data->buff_pos -= 1;
width = fish_wcwidth(data->command_line.at(data->buff_pos));
data->command_line.erase(data->buff_pos, 1);
} while (width == 0 && data->buff_pos > 0);
data->command_line_changed(); data->command_line_changed();
data->suppress_autosuggestion = true; data->suppress_autosuggestion = true;

View file

@ -300,7 +300,7 @@ static int calc_prompt_width( const wchar_t *prompt )
/* /*
Ordinary decent character. Just add width. Ordinary decent character. Just add width.
*/ */
res += wcwidth( prompt[j] ); res += fish_wcwidth( prompt[j] );
} }
} }
return res; return res;
@ -425,7 +425,7 @@ static void s_desired_append_char( screen_t *s,
case L'\n': case L'\n':
{ {
int i; int i;
s->desired.add_line(); s->desired.create_line(s->desired.line_count());
s->desired.cursor[1]++; s->desired.cursor[1]++;
s->desired.cursor[0]=0; s->desired.cursor[0]=0;
for( i=0; i < prompt_width+indent*INDENT_STEP; i++ ) for( i=0; i < prompt_width+indent*INDENT_STEP; i++ )
@ -438,7 +438,7 @@ static void s_desired_append_char( screen_t *s,
case L'\r': case L'\r':
{ {
line_t &current = s->desired.line(line_no); line_t &current = s->desired.line(line_no);
current.resize(0); current.clear();
s->desired.cursor[0] = 0; s->desired.cursor[0] = 0;
break; break;
} }
@ -446,8 +446,8 @@ static void s_desired_append_char( screen_t *s,
default: default:
{ {
int screen_width = common_get_width(); int screen_width = common_get_width();
int cw = wcwidth(b); int cw = fish_wcwidth(b);
int ew = wcwidth( ellipsis_char ); int ew = fish_wcwidth( ellipsis_char );
int i; int i;
s->desired.create_line(line_no); s->desired.create_line(line_no);
@ -458,9 +458,7 @@ static void s_desired_append_char( screen_t *s,
*/ */
if( s->desired.cursor[0] + cw + ew > screen_width ) if( s->desired.cursor[0] + cw + ew > screen_width )
{ {
line_entry_t &entry = s->desired.line(line_no).create_entry(s->desired.cursor[0]); s->desired.line(line_no).append(ellipsis_char, HIGHLIGHT_COMMENT);
entry.text = ellipsis_char;
entry.color = HIGHLIGHT_COMMENT;
line_no = s->desired.line_count(); line_no = s->desired.line_count();
s->desired.add_line(); s->desired.add_line();
@ -474,8 +472,7 @@ static void s_desired_append_char( screen_t *s,
} }
line_t &line = s->desired.line(line_no); line_t &line = s->desired.line(line_no);
line.create_entry(s->desired.cursor[0]).text = b; line.append(b, c);
line.create_entry(s->desired.cursor[0]).color = c;
s->desired.cursor[0]+= cw; s->desired.cursor[0]+= cw;
break; break;
} }
@ -553,7 +550,8 @@ static void s_move( screen_t *s, data_buffer_t *b, int new_x, int new_y )
x_steps = 0; x_steps = 0;
} }
if( x_steps < 0 ){ if( x_steps < 0 )
{
str = cursor_left; str = cursor_left;
} }
else else
@ -590,10 +588,23 @@ static void s_set_color( screen_t *s, data_buffer_t *b, int c )
static void s_write_char( screen_t *s, data_buffer_t *b, wchar_t c ) static void s_write_char( screen_t *s, data_buffer_t *b, wchar_t c )
{ {
scoped_buffer_t scoped_buffer(b); scoped_buffer_t scoped_buffer(b);
s->actual.cursor[0]+=wcwidth( c ); s->actual.cursor[0]+=fish_wcwidth( c );
writech( c ); writech( c );
} }
/**
Convert a wide string to a multibyte string and append it to the
buffer. Returns the width.
*/
static int s_write_string( screen_t *s, data_buffer_t *b, const wcstring &str )
{
scoped_buffer_t scoped_buffer(b);
int width = fish_wcswidth(str.c_str(), str.size());
writestr(str.c_str());
s->actual.cursor[0] += width;
return width;
}
/** /**
Send the specified string through tputs and append the output to Send the specified string through tputs and append the output to
the specified buffer. the specified buffer.
@ -614,14 +625,36 @@ static void s_write_str( data_buffer_t *b, const wchar_t *s )
writestr( s ); writestr( s );
} }
/** Returns the length of the "shared prefix" of the two lines, which is the run of matching text and colors.
If the prefix ends on a combining character, do not include the previous character in the prefix.
*/
static size_t line_shared_prefix(const line_t &a, const line_t &b)
{
size_t idx, max = std::min(a.size(), b.size());
for (idx=0; idx < max; idx++)
{
wchar_t ac = a.char_at(idx), bc = b.char_at(idx);
if (fish_wcwidth(ac) < 1 || fish_wcwidth(bc) < 1)
{
/* Possible combining mark, return one index prior */
if (idx > 0) idx--;
break;
}
/* We're done if the text or colors are different */
if (ac != bc || a.color_at(idx) != b.color_at(idx))
break;
}
return idx;
}
/** /**
Update the screen to match the desired output. Update the screen to match the desired output.
*/ */
static void s_update( screen_t *scr, const wchar_t *prompt ) static void s_update( screen_t *scr, const wchar_t *prompt )
{ {
size_t i, j; size_t i;
int prompt_width = calc_prompt_width( prompt ); int prompt_width = calc_prompt_width( prompt );
int current_width=0;
int screen_width = common_get_width(); int screen_width = common_get_width();
int need_clear = scr->need_clear; int need_clear = scr->need_clear;
data_buffer_t output; data_buffer_t output;
@ -646,74 +679,75 @@ static void s_update( screen_t *scr, const wchar_t *prompt )
for (i=0; i < scr->desired.line_count(); i++) for (i=0; i < scr->desired.line_count(); i++)
{ {
line_t &o_line = scr->desired.line(i); const line_t &o_line = scr->desired.line(i);
line_t &s_line = scr->actual.create_line(i); line_t &s_line = scr->actual.create_line(i);
int start_pos = (i==0?prompt_width:0); int start_pos = (i==0 ? prompt_width : 0);
current_width = start_pos; int current_width = 0;
if( need_clear ) if( need_clear )
{ {
s_move( scr, &output, start_pos, i ); s_move( scr, &output, start_pos, i );
s_write_mbs( &output, clr_eol); s_write_mbs( &output, clr_eol);
s_line.resize(0); s_line.clear();
} }
for( j=start_pos; j<o_line.entry_count(); j++) /* Note that skip_remaining is a width, not a character count */
{ int skip_remaining = start_pos;
line_entry_t &entry = o_line.entry(j);
wchar_t o = entry.text; /* Compute how much we should skip. At a minimum we skip over the prompt. But also skip over the shared prefix of what we want to output now, and what we output before, to avoid repeatedly outputting it. */
int o_c = entry.color; size_t shared_prefix = line_shared_prefix(o_line, s_line);
if( !o ) if (shared_prefix > 0)
continue; {
int prefix_width = fish_wcswidth(&o_line.text.at(0), shared_prefix);
if( s_line.entry_count() == j ) if (prefix_width > skip_remaining)
{ skip_remaining = prefix_width;
s_move( scr, &output, current_width, i ); }
s_set_color( scr, &output, o_c );
s_write_char( scr, &output, o ); /* Skip over skip_remaining width worth of characters */
s_line.create_entry(j).text = o; size_t j = 0;
s_line.create_entry(j).color = o_c; for ( ; j < o_line.size(); j++)
} {
else int width = fish_wcwidth(o_line.char_at(j));
{ skip_remaining -= width;
line_entry_t &entry = s_line.create_entry(j); if (skip_remaining <= 0)
wchar_t s = entry.text; break;
int s_c = entry.color; current_width += width;
}
if( o != s || o_c != s_c )
{ /* Skip over zero-width characters (e.g. combining marks at the end of the prompt) */
s_move( scr, &output, current_width, i ); for ( ; j < o_line.size(); j++)
s_set_color( scr, &output, o_c ); {
s_write_char( scr, &output, o ); int width = fish_wcwidth(o_line.char_at(j));
if (width > 0)
s_line.create_entry(current_width).text = o; break;
s_line.create_entry(current_width).color = o_c; }
for( int k=1; k<wcwidth(o); k++ )
s_line.create_entry(current_width+k).text = L'\0'; /* Now actually output stuff */
for ( ; j < o_line.size(); j++)
} {
} s_move( scr, &output, current_width, i );
current_width += wcwidth( o ); s_set_color( scr, &output, o_line.color_at(j) );
} s_write_char( scr, &output, o_line.char_at(j) );
current_width += fish_wcwidth(o_line.char_at(j));
if ( s_line.entry_count() > o_line.entry_count() ) }
/* If we wrote more on this line last time, clear it */
int prev_length = (s_line.text.empty() ? 0 : fish_wcswidth(&s_line.text.at(0), s_line.text.size()));
if (prev_length > current_width )
{ {
s_move( scr, &output, current_width, i ); s_move( scr, &output, current_width, i );
s_write_mbs( &output, clr_eol); s_write_mbs( &output, clr_eol);
s_line.resize(o_line.entry_count());
} }
} }
/* Clear remaining lines */
for( i=scr->desired.line_count(); i < scr->actual.line_count(); i++ ) for( i=scr->desired.line_count(); i < scr->actual.line_count(); i++ )
{ {
line_t &s_line = scr->actual.create_line(i);
s_move( scr, &output, 0, i ); s_move( scr, &output, 0, i );
s_write_mbs( &output, clr_eol); s_write_mbs( &output, clr_eol);
s_line.resize(0);
} }
s_move( scr, &output, scr->desired.cursor[0], scr->desired.cursor[1] ); s_move( scr, &output, scr->desired.cursor[0], scr->desired.cursor[1] );
s_set_color( scr, &output, 0xffffffff); s_set_color( scr, &output, 0xffffffff);
if( ! output.empty() ) if( ! output.empty() )
@ -721,6 +755,8 @@ static void s_update( screen_t *scr, const wchar_t *prompt )
write_loop( 1, &output.at(0), output.size() ); write_loop( 1, &output.at(0), output.size() );
} }
/* We have now synced our actual screen against our desired screen. Note that this is a big assignment! */
scr->actual = scr->desired;
} }
/** /**
@ -815,7 +851,7 @@ void s_write( screen_t *s,
} }
else else
{ {
int width = wcwidth(commandline[i]); int width = fish_wcwidth(commandline[i]);
current_line_width += width; current_line_width += width;
if (i < explicit_len) if (i < explicit_len)
explicit_portion_width += width; explicit_portion_width += width;
@ -884,7 +920,7 @@ void s_write( screen_t *s,
cursor won't be on the ellipsis which looks cursor won't be on the ellipsis which looks
unintuitive. unintuitive.
*/ */
cursor_arr[0] = s->desired.cursor[0] - wcwidth(commandline[i]); cursor_arr[0] = s->desired.cursor[0] - fish_wcwidth(commandline[i]);
cursor_arr[1] = s->desired.cursor[1]; cursor_arr[1] = s->desired.cursor[1];
} }

View file

@ -14,38 +14,41 @@
#include <vector> #include <vector>
struct line_entry_t
{
wchar_t text;
int color;
};
/** /**
A class representing a single line of a screen. A class representing a single line of a screen.
*/ */
class line_t struct line_t
{ {
public: std::vector<wchar_t> text;
std::vector<struct line_entry_t> entries; std::vector<int> colors;
void resize(size_t size) { void clear(void)
entries.resize(size); {
text.clear();
colors.clear();
} }
line_entry_t &entry(size_t idx) { void append(wchar_t txt, int color)
return entries.at(idx); {
text.push_back(txt);
colors.push_back(color);
}
size_t size(void) const
{
return text.size();
} }
line_entry_t &create_entry(size_t idx) { wchar_t char_at(size_t idx) const
if (idx >= entries.size()) { {
entries.resize(idx + 1); return text.at(idx);
}
return entries.at(idx);
} }
size_t entry_count(void) { int color_at(size_t idx) const
return entries.size(); {
return colors.at(idx);
} }
}; };
/** /**
@ -67,7 +70,7 @@ class screen_data_t
void resize(size_t size) { void resize(size_t size) {
line_datas.resize(size); line_datas.resize(size);
} }
line_t &create_line(size_t idx) { line_t &create_line(size_t idx) {
if (idx >= line_datas.size()) { if (idx >= line_datas.size()) {
line_datas.resize(idx + 1); line_datas.resize(idx + 1);