mirror of
https://github.com/fish-shell/fish-shell
synced 2024-12-26 12:53:13 +00:00
Work on soft wrapping to address https://github.com/fish-shell/fish-shell/issues/300
Resizing is still wonky
This commit is contained in:
parent
ffc03735e8
commit
a661c03743
3 changed files with 111 additions and 35 deletions
|
@ -1199,7 +1199,7 @@ static void update_autosuggestion(void) {
|
||||||
if (can_autosuggest()) {
|
if (can_autosuggest()) {
|
||||||
history_search_t searcher = history_search_t(*data->history, data->command_line, HISTORY_SEARCH_TYPE_PREFIX);
|
history_search_t searcher = history_search_t(*data->history, data->command_line, HISTORY_SEARCH_TYPE_PREFIX);
|
||||||
if (searcher.go_backwards()) {
|
if (searcher.go_backwards()) {
|
||||||
data->autosuggestion = searcher.current_item();
|
data->autosuggestion = searcher.current_item().str();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
|
|
92
screen.cpp
92
screen.cpp
|
@ -59,6 +59,9 @@ efficient way for transforming that to the desired screen content.
|
||||||
*/
|
*/
|
||||||
#define INDENT_STEP 4
|
#define INDENT_STEP 4
|
||||||
|
|
||||||
|
/** A helper value for an invalid location */
|
||||||
|
#define INVALID_LOCATION (screen_data_t::cursor_t(-1, -1))
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Ugly kludge. The internal buffer used to store output of
|
Ugly kludge. The internal buffer used to store output of
|
||||||
tputs. Since tputs external function can only take an integer and
|
tputs. Since tputs external function can only take an integer and
|
||||||
|
@ -123,7 +126,8 @@ static size_t next_tab_stop( size_t in )
|
||||||
}
|
}
|
||||||
|
|
||||||
// PCA for term256 support, let's just detect the escape codes directly
|
// PCA for term256 support, let's just detect the escape codes directly
|
||||||
static int is_term256_escape(const wchar_t *str) {
|
static int is_term256_escape(const wchar_t *str)
|
||||||
|
{
|
||||||
// An escape code looks like this: \x1b[38;5;<num>m
|
// An escape code looks like this: \x1b[38;5;<num>m
|
||||||
// or like this: \x1b[48;5;<num>m
|
// or like this: \x1b[48;5;<num>m
|
||||||
|
|
||||||
|
@ -144,6 +148,13 @@ static int is_term256_escape(const wchar_t *str) {
|
||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Whether we permit soft wrapping. If so, in some cases we don't explicitly move to the second physical line on a wrapped logical line; instead we just output it. */
|
||||||
|
static bool allow_soft_wrap(void)
|
||||||
|
{
|
||||||
|
// Should we be looking at eat_newline_glitch as well?
|
||||||
|
return !! auto_right_margin;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Calculate the width of the specified prompt. Does some clever magic
|
Calculate the width of the specified prompt. Does some clever magic
|
||||||
to detect common escape sequences that may be embeded in a prompt,
|
to detect common escape sequences that may be embeded in a prompt,
|
||||||
|
@ -423,6 +434,8 @@ static void s_desired_append_char( screen_t *s,
|
||||||
case L'\n':
|
case L'\n':
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
/* Current line is definitely hard wrapped */
|
||||||
|
s->desired.line(s->desired.cursor.y).is_soft_wrapped = false;
|
||||||
s->desired.create_line(s->desired.line_count());
|
s->desired.create_line(s->desired.line_count());
|
||||||
s->desired.cursor.y++;
|
s->desired.cursor.y++;
|
||||||
s->desired.cursor.x=0;
|
s->desired.cursor.x=0;
|
||||||
|
@ -453,6 +466,10 @@ static void s_desired_append_char( screen_t *s,
|
||||||
*/
|
*/
|
||||||
if( (s->desired.cursor.x + cw) > screen_width )
|
if( (s->desired.cursor.x + cw) > screen_width )
|
||||||
{
|
{
|
||||||
|
/* Current line is soft wrapped (assuming we support it) */
|
||||||
|
s->desired.line(s->desired.cursor.y).is_soft_wrapped = true;
|
||||||
|
//fprintf(stderr, "\n\n1 Soft wrapping %d\n\n", s->desired.cursor.y);
|
||||||
|
|
||||||
line_no = (int)s->desired.line_count();
|
line_no = (int)s->desired.line_count();
|
||||||
s->desired.add_line();
|
s->desired.add_line();
|
||||||
s->desired.cursor.y++;
|
s->desired.cursor.y++;
|
||||||
|
@ -466,6 +483,14 @@ 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.append(b, c);
|
line.append(b, c);
|
||||||
s->desired.cursor.x+= cw;
|
s->desired.cursor.x+= cw;
|
||||||
|
|
||||||
|
/* Maybe wrap the cursor to the next line, even if the line itself did not wrap. This avoids wonkiness in the last column. */
|
||||||
|
if (s->desired.cursor.x >= screen_width)
|
||||||
|
{
|
||||||
|
line.is_soft_wrapped = true;
|
||||||
|
s->desired.cursor.x = 0;
|
||||||
|
s->desired.cursor.y++;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -493,6 +518,9 @@ static int s_writeb( char c )
|
||||||
*/
|
*/
|
||||||
static void s_move( screen_t *s, data_buffer_t *b, int new_x, int new_y )
|
static void s_move( screen_t *s, data_buffer_t *b, int new_x, int new_y )
|
||||||
{
|
{
|
||||||
|
if (s->actual.cursor.x == new_x && s->actual.cursor.y == new_y)
|
||||||
|
return;
|
||||||
|
|
||||||
int i;
|
int i;
|
||||||
int x_steps, y_steps;
|
int x_steps, y_steps;
|
||||||
|
|
||||||
|
@ -582,19 +610,19 @@ 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.x+=fish_wcwidth( c );
|
s->actual.cursor.x+=fish_wcwidth( c );
|
||||||
writech( c );
|
writech( c );
|
||||||
}
|
if (s->actual.cursor.x == s->actual_width && allow_soft_wrap())
|
||||||
|
{
|
||||||
/**
|
s->soft_wrap_location.x = 0;
|
||||||
Convert a wide string to a multibyte string and append it to the
|
s->soft_wrap_location.y = s->actual.cursor.y + 1;
|
||||||
buffer. Returns the width.
|
|
||||||
*/
|
/* If auto_right_margin is set, then the cursor sticks to the right edge when it's in the rightmost column, so reflect that fact */
|
||||||
static int s_write_string( screen_t *s, data_buffer_t *b, const wcstring &str )
|
if (auto_right_margin)
|
||||||
{
|
s->actual.cursor.x--;
|
||||||
scoped_buffer_t scoped_buffer(b);
|
}
|
||||||
int width = fish_wcswidth(str.c_str(), str.size());
|
else
|
||||||
writestr(str.c_str());
|
{
|
||||||
s->actual.cursor.x += width;
|
s->soft_wrap_location = INVALID_LOCATION;
|
||||||
return width;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -640,6 +668,21 @@ static size_t line_shared_prefix(const line_t &a, const line_t &b)
|
||||||
return idx;
|
return idx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* We are about to output one or more characters onto the screen at the given x, y. If we are at the end of previous line, and the previous line is marked as soft wrapping, then tweak the screen so we believe we are already in the target position. This lets the terminal take care of wrapping, which means that if you copy and paste the text, it won't have an embedded newline. */
|
||||||
|
static bool perform_any_impending_soft_wrap(screen_t *scr, int x, int y)
|
||||||
|
{
|
||||||
|
if (x == scr->soft_wrap_location.x && y == scr->soft_wrap_location.y)
|
||||||
|
{
|
||||||
|
/* We can soft wrap; but do we want to? */
|
||||||
|
if (scr->desired.line(y - 1).is_soft_wrapped && allow_soft_wrap())
|
||||||
|
{
|
||||||
|
/* Yes. Just update the actual cursor; that will cause us to elide emitting the commands to move here, so we will just output on "one big line" (which the terminal soft wraps */
|
||||||
|
scr->actual.cursor = scr->soft_wrap_location;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Update the screen to match the desired output.
|
Update the screen to match the desired output.
|
||||||
*/
|
*/
|
||||||
|
@ -687,6 +730,7 @@ static void s_update( screen_t *scr, const wchar_t *prompt )
|
||||||
|
|
||||||
/* 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. */
|
/* 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. */
|
||||||
size_t shared_prefix = line_shared_prefix(o_line, s_line);
|
size_t shared_prefix = line_shared_prefix(o_line, s_line);
|
||||||
|
|
||||||
if (shared_prefix > 0)
|
if (shared_prefix > 0)
|
||||||
{
|
{
|
||||||
int prefix_width = fish_wcswidth(&o_line.text.at(0), shared_prefix);
|
int prefix_width = fish_wcswidth(&o_line.text.at(0), shared_prefix);
|
||||||
|
@ -694,12 +738,17 @@ static void s_update( screen_t *scr, const wchar_t *prompt )
|
||||||
skip_remaining = prefix_width;
|
skip_remaining = prefix_width;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Don't skip over the last two characters in a soft-wrapped line, so that we maintain soft-wrapping */
|
||||||
|
if (o_line.is_soft_wrapped)
|
||||||
|
skip_remaining = mini(skip_remaining, (size_t)(scr->actual_width - 2));
|
||||||
|
|
||||||
|
|
||||||
/* Skip over skip_remaining width worth of characters */
|
/* Skip over skip_remaining width worth of characters */
|
||||||
size_t j = 0;
|
size_t j = 0;
|
||||||
for ( ; j < o_line.size(); j++)
|
for ( ; j < o_line.size(); j++)
|
||||||
{
|
{
|
||||||
int width = fish_wcwidth(o_line.char_at(j));
|
int width = fish_wcwidth(o_line.char_at(j));
|
||||||
if (skip_remaining <= width)
|
if (skip_remaining < width)
|
||||||
break;
|
break;
|
||||||
skip_remaining -= width;
|
skip_remaining -= width;
|
||||||
current_width += width;
|
current_width += width;
|
||||||
|
@ -716,6 +765,7 @@ static void s_update( screen_t *scr, const wchar_t *prompt )
|
||||||
/* Now actually output stuff */
|
/* Now actually output stuff */
|
||||||
for ( ; j < o_line.size(); j++)
|
for ( ; j < o_line.size(); j++)
|
||||||
{
|
{
|
||||||
|
perform_any_impending_soft_wrap(scr, current_width, (int)i);
|
||||||
s_move( scr, &output, current_width, (int)i );
|
s_move( scr, &output, current_width, (int)i );
|
||||||
s_set_color( scr, &output, o_line.color_at(j) );
|
s_set_color( scr, &output, o_line.color_at(j) );
|
||||||
s_write_char( scr, &output, o_line.char_at(j) );
|
s_write_char( scr, &output, o_line.char_at(j) );
|
||||||
|
@ -748,6 +798,7 @@ static void s_update( screen_t *scr, const wchar_t *prompt )
|
||||||
|
|
||||||
/* We have now synced our actual screen against our desired screen. Note that this is a big assignment! */
|
/* We have now synced our actual screen against our desired screen. Note that this is a big assignment! */
|
||||||
scr->actual = scr->desired;
|
scr->actual = scr->desired;
|
||||||
|
auto_right_margin;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -932,3 +983,14 @@ void s_reset( screen_t *s, bool reset_cursor )
|
||||||
fstat( 2, &s->prev_buff_2 );
|
fstat( 2, &s->prev_buff_2 );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
screen_t::screen_t() :
|
||||||
|
desired(),
|
||||||
|
actual(),
|
||||||
|
actual_prompt(),
|
||||||
|
actual_width(0),
|
||||||
|
soft_wrap_location(INVALID_LOCATION),
|
||||||
|
need_clear(0),
|
||||||
|
prev_buff_1(), prev_buff_2(), post_buff_1(), post_buff_2()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
52
screen.h
52
screen.h
|
@ -21,6 +21,11 @@ struct line_t
|
||||||
{
|
{
|
||||||
std::vector<wchar_t> text;
|
std::vector<wchar_t> text;
|
||||||
std::vector<int> colors;
|
std::vector<int> colors;
|
||||||
|
bool is_soft_wrapped;
|
||||||
|
|
||||||
|
line_t() : text(), colors(), is_soft_wrapped(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
void clear(void)
|
void clear(void)
|
||||||
{
|
{
|
||||||
|
@ -63,6 +68,8 @@ class screen_data_t
|
||||||
struct cursor_t {
|
struct cursor_t {
|
||||||
int x;
|
int x;
|
||||||
int y;
|
int y;
|
||||||
|
cursor_t() : x(0), y(0) { }
|
||||||
|
cursor_t(int a, int b) : x(a), y(b) { }
|
||||||
} cursor;
|
} cursor;
|
||||||
|
|
||||||
line_t &add_line(void) {
|
line_t &add_line(void) {
|
||||||
|
@ -96,28 +103,35 @@ class screen_data_t
|
||||||
class screen_t
|
class screen_t
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/**
|
|
||||||
The internal representation of the desired screen contents.
|
|
||||||
*/
|
|
||||||
screen_data_t desired;
|
|
||||||
/**
|
|
||||||
The internal representation of the actual screen contents.
|
|
||||||
*/
|
|
||||||
screen_data_t actual;
|
|
||||||
|
|
||||||
/**
|
/** Constructor */
|
||||||
A string containing the prompt which was last printed to
|
screen_t();
|
||||||
the screen.
|
|
||||||
*/
|
|
||||||
wcstring actual_prompt;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
The actual width of the screen at the time of the last screen
|
The internal representation of the desired screen contents.
|
||||||
write.
|
*/
|
||||||
*/
|
screen_data_t desired;
|
||||||
int actual_width;
|
/**
|
||||||
|
The internal representation of the actual screen contents.
|
||||||
|
*/
|
||||||
|
screen_data_t actual;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
A string containing the prompt which was last printed to
|
||||||
|
the screen.
|
||||||
|
*/
|
||||||
|
wcstring actual_prompt;
|
||||||
|
|
||||||
|
/**
|
||||||
|
The actual width of the screen at the time of the last screen
|
||||||
|
write.
|
||||||
|
*/
|
||||||
|
int actual_width;
|
||||||
|
|
||||||
|
/** If we support soft wrapping, we can output to this location without any cursor motion. */
|
||||||
|
screen_data_t::cursor_t soft_wrap_location;
|
||||||
|
|
||||||
|
/**
|
||||||
This flag is set to true when there is reason to suspect that
|
This flag is set to true when there is reason to suspect that
|
||||||
the parts of the screen lines where the actual content is not
|
the parts of the screen lines where the actual content is not
|
||||||
filled in may be non-empty. This means that a clr_eol command
|
filled in may be non-empty. This means that a clr_eol command
|
||||||
|
|
Loading…
Reference in a new issue