mirror of
https://github.com/fish-shell/fish-shell
synced 2024-11-11 07:34:32 +00:00
Support for fish_right_prompt
Fixes https://github.com/fish-shell/fish-shell/issues/80
This commit is contained in:
parent
21e83a881e
commit
d76f880faf
9 changed files with 499 additions and 123 deletions
|
@ -2320,7 +2320,7 @@ static int builtin_read( parser_t &parser, wchar_t **argv )
|
|||
const wchar_t *line;
|
||||
|
||||
reader_push( mode_name );
|
||||
reader_set_prompt( prompt );
|
||||
reader_set_left_prompt( prompt );
|
||||
if( shell )
|
||||
{
|
||||
reader_set_complete_function( &complete );
|
||||
|
|
|
@ -1776,7 +1776,7 @@ void complete( const wcstring &cmd, std::vector<completion_t> &comps, complete_t
|
|||
{
|
||||
|
||||
const wcstring ncmd = tok_last( &tok );
|
||||
int is_ddash = (ncmd == L"--") && ( (tok_get_pos( &tok )+2) < pos );
|
||||
int is_ddash = (ncmd == L"--") && ( (tok_get_pos( &tok )+2) < (long)pos );
|
||||
|
||||
if( !had_cmd )
|
||||
{
|
||||
|
@ -1842,7 +1842,7 @@ void complete( const wcstring &cmd, std::vector<completion_t> &comps, complete_t
|
|||
|
||||
}
|
||||
|
||||
if( tok_get_pos( &tok ) >= pos )
|
||||
if( tok_get_pos( &tok ) >= (long)pos )
|
||||
{
|
||||
end_loop=1;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
\section fish_prompt fish_prompt - define the apperance of the command line prompt
|
||||
|
||||
\subsection fish_promt-synopsis Synopsis
|
||||
\subsection fish_prompt-synopsis Synopsis
|
||||
<pre>function fish_prompt
|
||||
...
|
||||
end</pre>
|
||||
|
|
23
doc_src/fish_right_prompt.txt
Normal file
23
doc_src/fish_right_prompt.txt
Normal file
|
@ -0,0 +1,23 @@
|
|||
\section fish_right_prompt fish_right_prompt - define the apperance of the right-side command line prompt
|
||||
|
||||
\subsection fish_right_prompt-synopsis Synopsis
|
||||
<pre>function fish_right_prompt
|
||||
...
|
||||
end</pre>
|
||||
|
||||
\subsection fish_right_prompt-description Description
|
||||
|
||||
\c fish_right_prompt is similar to \c fish_prompt, except that it appears on the right side of the terminal window.
|
||||
|
||||
Multiple lines are not supported in \c fish_right_prompt.
|
||||
|
||||
\subsection fish_prompt-example Example
|
||||
|
||||
A simple right prompt:
|
||||
|
||||
<pre>
|
||||
function fish_right_prompt -d "Write out the right prompt"
|
||||
date "+%m/%d/%y"
|
||||
end
|
||||
</pre>
|
||||
|
|
@ -95,7 +95,7 @@ struct terminfo_mapping_t
|
|||
/**
|
||||
Names of all the input functions supported
|
||||
*/
|
||||
static const wchar_t *name_arr[] =
|
||||
static const wchar_t * const name_arr[] =
|
||||
{
|
||||
L"beginning-of-line",
|
||||
L"end-of-line",
|
||||
|
|
127
reader.cpp
127
reader.cpp
|
@ -123,7 +123,13 @@ commence.
|
|||
/**
|
||||
The name of the function that prints the fish prompt
|
||||
*/
|
||||
#define PROMPT_FUNCTION_NAME L"fish_prompt"
|
||||
#define LEFT_PROMPT_FUNCTION_NAME L"fish_prompt"
|
||||
|
||||
/**
|
||||
The name of the function that prints the fish right prompt (RPROMPT)
|
||||
*/
|
||||
#define RIGHT_PROMPT_FUNCTION_NAME L"fish_right_prompt"
|
||||
|
||||
|
||||
/**
|
||||
The default title for the reader. This is used by reader_readline.
|
||||
|
@ -242,8 +248,9 @@ class reader_data_t
|
|||
/** Name of the current application */
|
||||
wcstring app_name;
|
||||
|
||||
/** The prompt command */
|
||||
wcstring prompt;
|
||||
/** The prompt commands */
|
||||
wcstring left_prompt;
|
||||
wcstring right_prompt;
|
||||
|
||||
/** The output of the last evaluation of the prompt command */
|
||||
wcstring left_prompt_buff;
|
||||
|
@ -457,14 +464,15 @@ static void reader_repaint()
|
|||
std::vector<int> indents = data->indents;
|
||||
indents.resize(len);
|
||||
|
||||
s_write( &data->screen,
|
||||
data->left_prompt_buff.c_str(),
|
||||
data->right_prompt_buff.c_str(),
|
||||
full_line.c_str(),
|
||||
data->command_line.size(),
|
||||
&colors[0],
|
||||
&indents[0],
|
||||
data->buff_pos );
|
||||
s_write( &data->screen,
|
||||
data->left_prompt_buff,
|
||||
data->right_prompt_buff,
|
||||
full_line,
|
||||
data->command_length(),
|
||||
&colors[0],
|
||||
&indents[0],
|
||||
data->buff_pos);
|
||||
|
||||
data->repaint_needed = false;
|
||||
}
|
||||
|
||||
|
@ -648,31 +656,46 @@ void reader_write_title()
|
|||
*/
|
||||
static void exec_prompt()
|
||||
{
|
||||
wcstring_list_t prompt_list;
|
||||
|
||||
if( data->prompt.size() )
|
||||
{
|
||||
proc_push_interactive( 0 );
|
||||
|
||||
if( exec_subshell( data->prompt, prompt_list ) == -1 )
|
||||
{
|
||||
/* If executing the prompt fails, make sure we at least don't print any junk */
|
||||
prompt_list.clear();
|
||||
}
|
||||
proc_pop_interactive();
|
||||
}
|
||||
|
||||
reader_write_title();
|
||||
|
||||
/* Clear existing prompts */
|
||||
data->left_prompt_buff.clear();
|
||||
data->right_prompt_buff.clear();
|
||||
|
||||
for( size_t i = 0; i < prompt_list.size(); i++ )
|
||||
{
|
||||
if (i > 0) data->left_prompt_buff += L'\n';
|
||||
data->left_prompt_buff += prompt_list.at(i);
|
||||
}
|
||||
/* If we have any prompts, they must be run non-interactively */
|
||||
if (data->left_prompt.size() || data->right_prompt.size())
|
||||
{
|
||||
proc_push_interactive( 0 );
|
||||
|
||||
data->right_prompt_buff = L"This is my right prompt";
|
||||
if (data->left_prompt.size())
|
||||
{
|
||||
wcstring_list_t prompt_list;
|
||||
if( exec_subshell( data->left_prompt, prompt_list ) == 0 )
|
||||
{
|
||||
for( size_t i = 0; i < prompt_list.size(); i++ )
|
||||
{
|
||||
if (i > 0) data->left_prompt_buff += L'\n';
|
||||
data->left_prompt_buff += prompt_list.at(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (data->right_prompt.size())
|
||||
{
|
||||
wcstring_list_t prompt_list;
|
||||
if( exec_subshell( data->right_prompt, prompt_list ) == 0 )
|
||||
{
|
||||
for( size_t i = 0; i < prompt_list.size(); i++ )
|
||||
{
|
||||
// Right prompt does not support multiple lines, so just concatenate all of them
|
||||
data->right_prompt_buff += prompt_list.at(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
proc_pop_interactive();
|
||||
}
|
||||
|
||||
/* Write the screen title */
|
||||
reader_write_title();
|
||||
}
|
||||
|
||||
void reader_init()
|
||||
|
@ -2124,10 +2147,19 @@ int reader_shell_test( const wchar_t *b )
|
|||
{
|
||||
wcstring sb;
|
||||
|
||||
int tmp[1];
|
||||
int tmp2[1];
|
||||
const int tmp[1] = {0};
|
||||
const int tmp2[1] = {0};
|
||||
const wcstring empty;
|
||||
|
||||
s_write( &data->screen,
|
||||
empty,
|
||||
empty,
|
||||
empty,
|
||||
0,
|
||||
tmp,
|
||||
tmp2,
|
||||
0);
|
||||
|
||||
s_write( &data->screen, L"", L"", L"", 0, tmp, tmp2, 0 );
|
||||
|
||||
parser_t::principal_parser().test( b, 0, &sb, L"fish" );
|
||||
fwprintf( stderr, L"%ls", sb.c_str() );
|
||||
|
@ -2165,7 +2197,7 @@ void reader_push( const wchar_t *name )
|
|||
exec_prompt();
|
||||
reader_set_highlight_function( &highlight_universal );
|
||||
reader_set_test_function( &default_test );
|
||||
reader_set_prompt( L"" );
|
||||
reader_set_left_prompt( L"" );
|
||||
}
|
||||
|
||||
void reader_pop()
|
||||
|
@ -2196,9 +2228,14 @@ void reader_pop()
|
|||
}
|
||||
}
|
||||
|
||||
void reader_set_prompt( const wchar_t *new_prompt )
|
||||
void reader_set_left_prompt( const wcstring &new_prompt )
|
||||
{
|
||||
data->prompt = new_prompt;
|
||||
data->left_prompt = new_prompt;
|
||||
}
|
||||
|
||||
void reader_set_right_prompt(const wcstring &new_prompt)
|
||||
{
|
||||
data->right_prompt = new_prompt;
|
||||
}
|
||||
|
||||
void reader_set_complete_function( complete_function_t f )
|
||||
|
@ -2454,10 +2491,16 @@ static int read_i()
|
|||
const wchar_t *tmp;
|
||||
|
||||
event_fire_generic(L"fish_prompt");
|
||||
if( function_exists( PROMPT_FUNCTION_NAME ) )
|
||||
reader_set_prompt( PROMPT_FUNCTION_NAME );
|
||||
if( function_exists( LEFT_PROMPT_FUNCTION_NAME ) )
|
||||
reader_set_left_prompt( LEFT_PROMPT_FUNCTION_NAME );
|
||||
else
|
||||
reader_set_prompt( DEFAULT_PROMPT );
|
||||
reader_set_left_prompt( DEFAULT_PROMPT );
|
||||
|
||||
if( function_exists( RIGHT_PROMPT_FUNCTION_NAME ) )
|
||||
reader_set_right_prompt( RIGHT_PROMPT_FUNCTION_NAME );
|
||||
else
|
||||
reader_set_right_prompt( L"" );
|
||||
|
||||
|
||||
/*
|
||||
Put buff in temporary string and clear buff, so
|
||||
|
|
8
reader.h
8
reader.h
|
@ -169,7 +169,13 @@ void reader_set_test_function( int (*f)( const wchar_t * ) );
|
|||
Specify string of shell commands to be run in order to generate the
|
||||
prompt.
|
||||
*/
|
||||
void reader_set_prompt( const wchar_t *prompt );
|
||||
void reader_set_left_prompt( const wcstring &prompt );
|
||||
|
||||
/**
|
||||
Specify string of shell commands to be run in order to generate the
|
||||
right prompt.
|
||||
*/
|
||||
void reader_set_right_prompt( const wcstring &prompt );
|
||||
|
||||
/**
|
||||
Returns true if the shell is exiting, 0 otherwise.
|
||||
|
|
424
screen.cpp
424
screen.cpp
|
@ -722,6 +722,7 @@ static void s_update( screen_t *scr, const wchar_t *left_prompt, const wchar_t *
|
|||
|
||||
int screen_width = common_get_width();
|
||||
bool need_clear = scr->need_clear;
|
||||
bool has_cleared_screen = false;
|
||||
|
||||
/* Figure out how many following lines we need to clear (probably 0) */
|
||||
size_t actual_lines_before_reset = scr->actual_lines_before_reset;
|
||||
|
@ -736,9 +737,17 @@ static void s_update( screen_t *scr, const wchar_t *left_prompt, const wchar_t *
|
|||
need_clear = true;
|
||||
s_move( scr, &output, 0, 0 );
|
||||
scr->actual_width = screen_width;
|
||||
s_reset( scr, false );
|
||||
s_reset( scr, false, false /* don't clear prompt */);
|
||||
}
|
||||
|
||||
/* Determine how many lines have stuff on them; we need to clear lines with stuff that we don't want */
|
||||
const size_t lines_with_stuff = maxi(actual_lines_before_reset, scr->actual.line_count());
|
||||
if (lines_with_stuff > scr->desired.line_count())
|
||||
{
|
||||
/* There are lines that we output to previously that will need to be cleared */
|
||||
need_clear = true;
|
||||
}
|
||||
|
||||
if( wcscmp( left_prompt, scr->actual_left_prompt.c_str() ) )
|
||||
{
|
||||
s_move( scr, &output, 0, 0 );
|
||||
|
@ -754,25 +763,28 @@ static void s_update( screen_t *scr, const wchar_t *left_prompt, const wchar_t *
|
|||
size_t start_pos = (i==0 ? left_prompt_width : 0);
|
||||
int current_width = 0;
|
||||
|
||||
/* If this is the last line, maybe we should clear the screen */
|
||||
const bool should_clear_screen_this_line = (need_clear && i + 1 == scr->desired.line_count() && clr_eos != NULL);
|
||||
|
||||
/* Note that skip_remaining is a width, not a character count */
|
||||
size_t skip_remaining = start_pos;
|
||||
|
||||
/* 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);
|
||||
/* Only skip the prompt if we're clearing the screen */
|
||||
if (! should_clear_screen_this_line) {
|
||||
/* 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. */
|
||||
const size_t shared_prefix = line_shared_prefix(o_line, s_line);
|
||||
if (! shared_prefix > 0)
|
||||
{
|
||||
int prefix_width = fish_wcswidth(&o_line.text.at(0), shared_prefix);
|
||||
if (prefix_width > skip_remaining)
|
||||
skip_remaining = prefix_width;
|
||||
}
|
||||
|
||||
if (shared_prefix > 0)
|
||||
{
|
||||
int prefix_width = fish_wcswidth(&o_line.text.at(0), shared_prefix);
|
||||
if (prefix_width > skip_remaining)
|
||||
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));
|
||||
}
|
||||
|
||||
/* 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_remaining = start_pos;
|
||||
|
||||
/* Skip over skip_remaining width worth of characters */
|
||||
size_t j = 0;
|
||||
for ( ; j < o_line.size(); j++)
|
||||
|
@ -792,6 +804,13 @@ static void s_update( screen_t *scr, const wchar_t *left_prompt, const wchar_t *
|
|||
break;
|
||||
}
|
||||
|
||||
/* Clear the screen before outputting. If we clear the screen after outputting, then we may erase the last character due to the sticky right margin. */
|
||||
if (should_clear_screen_this_line) {
|
||||
s_move( scr, &output, current_width, (int)i );
|
||||
s_write_mbs(&output, clr_eos);
|
||||
has_cleared_screen = true;
|
||||
}
|
||||
|
||||
/* Now actually output stuff */
|
||||
for ( ; j < o_line.size(); j++)
|
||||
{
|
||||
|
@ -804,7 +823,16 @@ static void s_update( screen_t *scr, const wchar_t *left_prompt, const wchar_t *
|
|||
|
||||
bool clear_remainder = false;
|
||||
/* Clear the remainder of the line if we need to clear and if we didn't write to the end of the line. If we did write to the end of the line, the "sticky right edge" (as part of auto_right_margin) means that we'll be clearing the last character we wrote! */
|
||||
if (need_clear && current_width < screen_width)
|
||||
if (has_cleared_screen)
|
||||
{
|
||||
/* Already cleared everything */
|
||||
clear_remainder = false;
|
||||
}
|
||||
else if (need_clear && current_width < screen_width)
|
||||
{
|
||||
clear_remainder = true;
|
||||
}
|
||||
else if (right_prompt_width < scr->last_right_prompt_width)
|
||||
{
|
||||
clear_remainder = true;
|
||||
}
|
||||
|
@ -820,25 +848,32 @@ static void s_update( screen_t *scr, const wchar_t *left_prompt, const wchar_t *
|
|||
s_write_mbs( &output, clr_eol);
|
||||
}
|
||||
|
||||
/* Output any rprompt if this is the first line. We do this at the end under the assumption that we will have to issue fewer commands this way. */
|
||||
/* Output any rprompt if this is the first line. */
|
||||
if (i == 0 && right_prompt_width > 0)
|
||||
{
|
||||
s_move( scr, &output, (int)(screen_width - right_prompt_width - 1), (int)i );
|
||||
s_move( scr, &output, (int)(screen_width - right_prompt_width), (int)i );
|
||||
s_set_color( scr, &output, 0xffffffff);
|
||||
s_write_str( &output, right_prompt );
|
||||
scr->actual.cursor.x += right_prompt_width;
|
||||
if (auto_right_margin) {
|
||||
/* Lame test for sticky right edge, which means that we output in the last column and the cursor did not advance to the next line, but remains in the last column. Every term I've tested has this behavior. */
|
||||
scr->actual.cursor.x--;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/* Determine how many lines have stuff on them; we need to clear lines with stuff that we don't want */
|
||||
size_t lines_with_stuff = maxi(actual_lines_before_reset, scr->actual.line_count());
|
||||
|
||||
/* Clear remaining lines */
|
||||
for( size_t i=scr->desired.line_count(); i < lines_with_stuff; i++ )
|
||||
{
|
||||
s_move( scr, &output, 0, (int)i );
|
||||
s_write_mbs( &output, clr_eol);
|
||||
}
|
||||
/* Clear remaining lines if we haven't done so already */
|
||||
if (need_clear && ! has_cleared_screen)
|
||||
{
|
||||
/* Clear remaining lines individually */
|
||||
for( size_t i=scr->desired.line_count(); i < lines_with_stuff; i++ )
|
||||
{
|
||||
s_move( scr, &output, 0, (int)i );
|
||||
s_write_mbs( &output, clr_eol);
|
||||
}
|
||||
}
|
||||
|
||||
s_move( scr, &output, scr->desired.cursor.x, scr->desired.cursor.y );
|
||||
s_set_color( scr, &output, 0xffffffff);
|
||||
|
@ -850,20 +885,276 @@ static void s_update( screen_t *scr, const wchar_t *left_prompt, const wchar_t *
|
|||
|
||||
/* We have now synced our actual screen against our desired screen. Note that this is a big assignment! */
|
||||
scr->actual = scr->desired;
|
||||
auto_right_margin;
|
||||
scr->last_right_prompt_width = right_prompt_width;
|
||||
}
|
||||
|
||||
/**
|
||||
Returns non-zero if we are using a dumb terminal.
|
||||
*/
|
||||
static int is_dumb()
|
||||
/** Returns true if we are using a dumb terminal. */
|
||||
static bool is_dumb(void)
|
||||
{
|
||||
return ( !cursor_up || !cursor_down || !cursor_left || !cursor_right );
|
||||
}
|
||||
|
||||
struct screen_layout_t {
|
||||
/* The left prompt that we're going to use */
|
||||
wcstring left_prompt;
|
||||
|
||||
/* How much space to leave for it */
|
||||
size_t left_prompt_space;
|
||||
|
||||
/* The right prompt */
|
||||
wcstring right_prompt;
|
||||
|
||||
/* The autosuggestion */
|
||||
wcstring autosuggestion;
|
||||
|
||||
/* Whether the prompts get their own line or not */
|
||||
bool prompts_get_own_line;
|
||||
};
|
||||
|
||||
/* Given a vector whose indexes are offsets and whose values are the widths of the string if truncated at that offset, return the offset that fits in the given width. Returns width_by_offset.size() - 1 if they all fit. The first value in width_by_offset is assumed to be 0. */
|
||||
static size_t truncation_offset_for_width(const std::vector<size_t> &width_by_offset, size_t max_width)
|
||||
{
|
||||
assert(width_by_offset.size() > 0 && width_by_offset.at(0) == 0);
|
||||
size_t i;
|
||||
for (i=1; i < width_by_offset.size(); i++)
|
||||
{
|
||||
if (width_by_offset.at(i) > max_width)
|
||||
break;
|
||||
}
|
||||
/* i is the first index that did not fit; i-1 is therefore the last that did */
|
||||
return i - 1;
|
||||
}
|
||||
|
||||
static screen_layout_t compute_layout(screen_t *s,
|
||||
size_t screen_width,
|
||||
const wcstring &left_prompt_str,
|
||||
const wcstring &right_prompt_str,
|
||||
const wcstring &commandline,
|
||||
const wcstring &autosuggestion_str,
|
||||
const int *indent)
|
||||
{
|
||||
screen_layout_t result = {};
|
||||
|
||||
/* Start by ensuring that the prompts themselves can fit */
|
||||
const wchar_t *left_prompt = left_prompt_str.c_str();
|
||||
const wchar_t *right_prompt = right_prompt_str.c_str();
|
||||
const wchar_t *autosuggestion = autosuggestion_str.c_str();
|
||||
|
||||
size_t left_prompt_width = calc_prompt_width( left_prompt );
|
||||
size_t right_prompt_width = calc_prompt_width( right_prompt );
|
||||
|
||||
if (left_prompt_width + right_prompt_width >= screen_width)
|
||||
{
|
||||
/* Nix right_prompt */
|
||||
right_prompt = L"";
|
||||
right_prompt_width = 0;
|
||||
}
|
||||
|
||||
if (left_prompt_width + right_prompt_width >= screen_width)
|
||||
{
|
||||
/* Still doesn't fit, neuter left_prompt */
|
||||
left_prompt = L"> ";
|
||||
left_prompt_width = 2;
|
||||
}
|
||||
|
||||
/* Now we should definitely fit */
|
||||
assert(left_prompt_width + right_prompt_width < screen_width);
|
||||
|
||||
|
||||
/* Convert commandline to a list of lines and their widths */
|
||||
wcstring_list_t command_lines(1);
|
||||
std::vector<size_t> line_widths(1);
|
||||
for (size_t i=0; i < commandline.size(); i++)
|
||||
{
|
||||
wchar_t c = commandline.at(i);
|
||||
if (c == L'\n')
|
||||
{
|
||||
/* Make a new line */
|
||||
command_lines.push_back(wcstring());
|
||||
line_widths.push_back(indent[i]*INDENT_STEP);
|
||||
}
|
||||
else
|
||||
{
|
||||
command_lines.back() += c;
|
||||
line_widths.back() += fish_wcwidth(c);
|
||||
}
|
||||
}
|
||||
const size_t first_command_line_width = line_widths.at(0);
|
||||
|
||||
/* If we have more than one line, ensure we have no autosuggestion */
|
||||
if (command_lines.size() > 1)
|
||||
{
|
||||
autosuggestion = L"";
|
||||
}
|
||||
|
||||
/* Compute the width of the autosuggestion at all possible truncation offsets */
|
||||
std::vector<size_t> autosuggestion_truncated_widths;
|
||||
autosuggestion_truncated_widths.reserve(1 + wcslen(autosuggestion));
|
||||
size_t autosuggestion_total_width = 0;
|
||||
for (size_t i=0; autosuggestion[i] != L'\0'; i++)
|
||||
{
|
||||
autosuggestion_truncated_widths.push_back(autosuggestion_total_width);
|
||||
autosuggestion_total_width += fish_wcwidth(autosuggestion[i]);
|
||||
}
|
||||
|
||||
/* Here are the layouts we try in turn:
|
||||
|
||||
1. Left prompt visible, right prompt visible, command line visible, autosuggestion visible
|
||||
2. Left prompt visible, right prompt visible, command line visible, autosuggestion truncated (possibly to zero)
|
||||
3. Left prompt visible, right prompt hidden, command line visible, autosuggestion hidden
|
||||
4. Newline separator (left prompt visible, right prompt visible, command line visible, autosuggestion visible)
|
||||
*/
|
||||
|
||||
bool done = false;
|
||||
|
||||
/* Case 1 */
|
||||
if (! done && left_prompt_width + right_prompt_width + first_command_line_width + autosuggestion_total_width + 10 < screen_width)
|
||||
{
|
||||
result.left_prompt = left_prompt;
|
||||
result.left_prompt_space = left_prompt_width;
|
||||
result.right_prompt = right_prompt;
|
||||
result.autosuggestion = autosuggestion;
|
||||
done = true;
|
||||
}
|
||||
|
||||
/* Case 2. Note that we require strict inequality so that there's always at least one space between the left edge and the rprompt */
|
||||
if (! done && left_prompt_width + right_prompt_width + first_command_line_width < screen_width)
|
||||
{
|
||||
result.left_prompt = left_prompt;
|
||||
result.left_prompt_space = left_prompt_width;
|
||||
result.right_prompt = right_prompt;
|
||||
|
||||
/* Need at least two characters to show an autosuggestion */
|
||||
size_t available_autosuggestion_space = screen_width - (left_prompt_width + right_prompt_width + first_command_line_width);
|
||||
if (autosuggestion_total_width > 0 && available_autosuggestion_space > 2)
|
||||
{
|
||||
size_t truncation_offset = truncation_offset_for_width(autosuggestion_truncated_widths, available_autosuggestion_space - 2);
|
||||
result.autosuggestion = wcstring(autosuggestion, truncation_offset);
|
||||
result.autosuggestion.push_back(ellipsis_char);
|
||||
}
|
||||
done = true;
|
||||
}
|
||||
|
||||
/* Case 3 */
|
||||
if (! done && left_prompt_width + first_command_line_width < screen_width)
|
||||
{
|
||||
result.left_prompt = left_prompt;
|
||||
result.left_prompt_space = left_prompt_width;
|
||||
done = true;
|
||||
}
|
||||
|
||||
/* Case 4 */
|
||||
if (! done)
|
||||
{
|
||||
result.left_prompt = left_prompt;
|
||||
result.left_prompt_space = left_prompt_width;
|
||||
result.right_prompt = right_prompt;
|
||||
result.prompts_get_own_line = true;
|
||||
done = true;
|
||||
}
|
||||
|
||||
assert(done);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
void s_write( screen_t *s,
|
||||
const wcstring &left_prompt,
|
||||
const wcstring &right_prompt,
|
||||
const wcstring &commandline,
|
||||
size_t explicit_len,
|
||||
const int *colors,
|
||||
const int *indent,
|
||||
size_t cursor_pos )
|
||||
{
|
||||
screen_data_t::cursor_t cursor_arr;
|
||||
|
||||
CHECK( s, );
|
||||
CHECK( indent, );
|
||||
|
||||
/* Turn the command line into the explicit portion and the autosuggestion */
|
||||
const wcstring explicit_command_line = commandline.substr(0, explicit_len);
|
||||
const wcstring autosuggestion = commandline.substr(explicit_len);
|
||||
|
||||
/*
|
||||
If we are using a dumb terminal, don't try any fancy stuff,
|
||||
just print out the text. right_prompt not supported.
|
||||
*/
|
||||
if( is_dumb() )
|
||||
{
|
||||
const std::string prompt_narrow = wcs2string( left_prompt );
|
||||
const std::string command_line_narrow = wcs2string( explicit_command_line );
|
||||
|
||||
write_loop( 1, "\r", 1 );
|
||||
write_loop( 1, prompt_narrow.c_str(), prompt_narrow.size() );
|
||||
write_loop( 1, command_line_narrow.c_str(), command_line_narrow.size() );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
s_check_status( s );
|
||||
const size_t screen_width = common_get_width();
|
||||
|
||||
/* Completely ignore impossibly small screens */
|
||||
if( screen_width < 4 )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* Compute a layout */
|
||||
const screen_layout_t layout = compute_layout(s, screen_width, left_prompt, right_prompt, explicit_command_line, autosuggestion, indent);
|
||||
|
||||
/* Clear the desired screen */
|
||||
s->desired.resize(0);
|
||||
s->desired.cursor.x = s->desired.cursor.y = 0;
|
||||
|
||||
/* 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 );
|
||||
}
|
||||
|
||||
/* 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 );
|
||||
first_line_prompt_space = 0;
|
||||
}
|
||||
|
||||
/* Reconstruct the command line */
|
||||
wcstring effective_commandline = explicit_command_line + layout.autosuggestion;
|
||||
|
||||
/* Output the command line */
|
||||
size_t i;
|
||||
for( i=0; i < effective_commandline.size(); i++ )
|
||||
{
|
||||
int color = colors[i];
|
||||
|
||||
if( i == cursor_pos )
|
||||
{
|
||||
color = 0;
|
||||
}
|
||||
|
||||
if( i == cursor_pos )
|
||||
{
|
||||
cursor_arr = s->desired.cursor;
|
||||
}
|
||||
|
||||
s_desired_append_char( s, effective_commandline.at(i), color, indent[i], first_line_prompt_space );
|
||||
}
|
||||
if( i == cursor_pos )
|
||||
{
|
||||
cursor_arr = s->desired.cursor;
|
||||
}
|
||||
|
||||
s->desired.cursor = cursor_arr;
|
||||
s_update( s, layout.left_prompt.c_str(), layout.right_prompt.c_str());
|
||||
s_save_status( s );
|
||||
}
|
||||
|
||||
|
||||
void s_write_OLD( screen_t *s,
|
||||
const wchar_t *left_prompt,
|
||||
const wchar_t *right_prompt,
|
||||
const wchar_t *commandline,
|
||||
|
@ -874,14 +1165,12 @@ void s_write( screen_t *s,
|
|||
{
|
||||
screen_data_t::cursor_t cursor_arr;
|
||||
|
||||
size_t prompt_width;
|
||||
size_t screen_width;
|
||||
|
||||
int current_line_width = 0, newline_count = 0, explicit_portion_width = 0;
|
||||
size_t max_line_width = 0;
|
||||
|
||||
CHECK( s, );
|
||||
CHECK( left_prompt, );
|
||||
CHECK( right_prompt, );
|
||||
CHECK( commandline, );
|
||||
CHECK( c, );
|
||||
CHECK( indent, );
|
||||
|
@ -905,8 +1194,9 @@ void s_write( screen_t *s,
|
|||
return;
|
||||
}
|
||||
|
||||
prompt_width = calc_prompt_width( left_prompt );
|
||||
screen_width = common_get_width();
|
||||
size_t left_prompt_width = calc_prompt_width( left_prompt );
|
||||
size_t right_prompt_width = calc_prompt_width( right_prompt );
|
||||
const size_t screen_width = common_get_width();
|
||||
|
||||
s_check_status( s );
|
||||
|
||||
|
@ -917,10 +1207,13 @@ void s_write( screen_t *s,
|
|||
It would be cool to truncate the prompt, but because it can
|
||||
contain escape sequences, this is harder than you'd think.
|
||||
*/
|
||||
if( prompt_width >= screen_width )
|
||||
if( left_prompt_width >= screen_width )
|
||||
{
|
||||
left_prompt = L"> ";
|
||||
prompt_width = 2;
|
||||
left_prompt_width = 2;
|
||||
|
||||
right_prompt = L"";
|
||||
right_prompt_width = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -951,7 +1244,7 @@ void s_write( screen_t *s,
|
|||
if (i < explicit_len)
|
||||
explicit_portion_width += width;
|
||||
|
||||
if (prompt_width + current_line_width < screen_width)
|
||||
if (left_prompt_width + current_line_width < screen_width)
|
||||
last_char_that_fits = i;
|
||||
}
|
||||
}
|
||||
|
@ -963,27 +1256,27 @@ void s_write( screen_t *s,
|
|||
|
||||
/* If we cannot fit with the autosuggestion, but we can fit without it, truncate the autosuggestion. We limit this check to just one line to avoid confusion; not sure how well this would work with multiple lines */
|
||||
wcstring truncated_autosuggestion_line;
|
||||
if (newline_count == 0 && prompt_width + max_line_width >= screen_width && prompt_width + explicit_portion_width < screen_width)
|
||||
if (newline_count == 0 && left_prompt_width + right_prompt_width + max_line_width >= screen_width && left_prompt_width + explicit_portion_width < screen_width)
|
||||
{
|
||||
assert(screen_width - prompt_width >= 1);
|
||||
max_line_width = screen_width - prompt_width - 1;
|
||||
truncated_autosuggestion_line = wcstring(commandline, 0, last_char_that_fits);
|
||||
assert(screen_width - left_prompt_width >= 1);
|
||||
max_line_width = screen_width - left_prompt_width - right_prompt_width - 1;
|
||||
truncated_autosuggestion_line = wcstring(commandline, 0, last_char_that_fits - right_prompt_width);
|
||||
truncated_autosuggestion_line.push_back(ellipsis_char);
|
||||
commandline = truncated_autosuggestion_line.c_str();
|
||||
}
|
||||
for( size_t i=0; i<prompt_width; i++ )
|
||||
for( size_t i=0; i<left_prompt_width; i++ )
|
||||
{
|
||||
s_desired_append_char( s, L' ', 0, 0, prompt_width );
|
||||
s_desired_append_char( s, L' ', 0, 0, left_prompt_width );
|
||||
}
|
||||
|
||||
/*
|
||||
If overflowing, give the prompt its own line to improve the
|
||||
situation.
|
||||
*/
|
||||
if( max_line_width + prompt_width >= screen_width )
|
||||
if( max_line_width + left_prompt_width >= screen_width )
|
||||
{
|
||||
s_desired_append_char( s, L'\n', 0, 0, 0 );
|
||||
prompt_width=0;
|
||||
left_prompt_width = 0;
|
||||
}
|
||||
|
||||
size_t i;
|
||||
|
@ -1001,19 +1294,7 @@ void s_write( screen_t *s,
|
|||
cursor_arr = s->desired.cursor;
|
||||
}
|
||||
|
||||
s_desired_append_char( s, commandline[i], col, indent[i], prompt_width );
|
||||
|
||||
if( i== cursor_pos && s->desired.cursor.y != cursor_arr.y && commandline[i] != L'\n' )
|
||||
{
|
||||
/*
|
||||
Ugh. We are placed exactly at the wrapping point of a
|
||||
wrapped line, move cursor to the line below so the
|
||||
cursor won't be on the ellipsis which looks
|
||||
unintuitive.
|
||||
*/
|
||||
cursor_arr.x = s->desired.cursor.x - fish_wcwidth(commandline[i]);
|
||||
cursor_arr.y = s->desired.cursor.y;
|
||||
}
|
||||
s_desired_append_char( s, commandline[i], col, indent[i], left_prompt_width );
|
||||
}
|
||||
if( i == cursor_pos )
|
||||
{
|
||||
|
@ -1021,29 +1302,37 @@ void s_write( screen_t *s,
|
|||
}
|
||||
|
||||
s->desired.cursor = cursor_arr;
|
||||
s_update( s, left_prompt, right_prompt ? right_prompt : L"" );
|
||||
s_update( s, left_prompt, right_prompt);
|
||||
s_save_status( s );
|
||||
}
|
||||
|
||||
void s_reset( screen_t *s, bool reset_cursor )
|
||||
void s_reset( screen_t *s, bool reset_cursor, bool reset_prompt )
|
||||
{
|
||||
CHECK( s, );
|
||||
|
||||
/* If we're resetting the cursor, we must also be resetting the prompt */
|
||||
assert(! reset_cursor || reset_prompt);
|
||||
|
||||
/* If we are resetting the cursor, we're going to make a new line and leave junk behind. If we are not resetting the cursor, we need to remember how many lines we had output to, so we can clear the remaining lines in the next call to s_update. This prevents leaving junk underneath the cursor when resizing a window wider such that it reduces our desired line count. */
|
||||
if (! reset_cursor)
|
||||
if (! reset_cursor) {
|
||||
s->actual_lines_before_reset = s->actual.line_count();
|
||||
}
|
||||
|
||||
int prev_line = s->actual.cursor.y;
|
||||
|
||||
/* If the prompt is multi-line, we need to move up to the prompt's initial line. We do this by lying to ourselves and claiming that we're really below what we consider "line 0" (which is the last line of the prompt). This will cause is to move up to try to get back to line 0, but really we're getting back to the initial line of the prompt. */
|
||||
const size_t prompt_line_count = calc_prompt_lines(s->actual_left_prompt.c_str());
|
||||
assert(prompt_line_count >= 1);
|
||||
prev_line += (prompt_line_count - 1);
|
||||
if (reset_prompt) {
|
||||
/* If the prompt is multi-line, we need to move up to the prompt's initial line. We do this by lying to ourselves and claiming that we're really below what we consider "line 0" (which is the last line of the prompt). This will cause is to move up to try to get back to line 0, but really we're getting back to the initial line of the prompt. */
|
||||
const size_t prompt_line_count = calc_prompt_lines(s->actual_left_prompt.c_str());
|
||||
assert(prompt_line_count >= 1);
|
||||
prev_line += (prompt_line_count - 1);
|
||||
|
||||
/* Clear the prompt */
|
||||
s->actual_left_prompt.clear();
|
||||
}
|
||||
|
||||
s->actual.resize(0);
|
||||
s->actual.cursor.x = 0;
|
||||
s->actual.cursor.y = 0;
|
||||
s->actual_left_prompt.clear();
|
||||
s->need_clear=true;
|
||||
|
||||
if( !reset_cursor )
|
||||
|
@ -1063,6 +1352,7 @@ screen_t::screen_t() :
|
|||
desired(),
|
||||
actual(),
|
||||
actual_left_prompt(),
|
||||
last_right_prompt_width(),
|
||||
actual_width(0),
|
||||
soft_wrap_location(INVALID_LOCATION),
|
||||
need_clear(false),
|
||||
|
|
18
screen.h
18
screen.h
|
@ -122,6 +122,9 @@ class screen_t
|
|||
*/
|
||||
wcstring actual_left_prompt;
|
||||
|
||||
/** Last right prompt width */
|
||||
size_t last_right_prompt_width;
|
||||
|
||||
/**
|
||||
The actual width of the screen at the time of the last screen
|
||||
write.
|
||||
|
@ -174,13 +177,24 @@ void s_write( screen_t *s,
|
|||
const int *indent,
|
||||
size_t cursor_pos );
|
||||
|
||||
|
||||
void s_write( screen_t *s,
|
||||
const wcstring &left_prompt,
|
||||
const wcstring &right_prompt,
|
||||
const wcstring &commandline,
|
||||
size_t explicit_len,
|
||||
const int *colors,
|
||||
const int *indent,
|
||||
size_t cursor_pos );
|
||||
|
||||
/**
|
||||
This function resets the screen buffers internal knowledge about
|
||||
the contents of the screen. Use this function when some other
|
||||
function than s_write has written to the screen.
|
||||
|
||||
\param s the screen to reset
|
||||
\param reset_cursor whether the line on which the curor has changed should be assumed to have changed. If \c reset_cursor is set to 0, the library will attempt to make sure that the screen area does not seem to move up or down on repaint.
|
||||
\param reset_cursor whether the line on which the curor has changed should be assumed to have changed. If \c reset_cursor is false, the library will attempt to make sure that the screen area does not seem to move up or down on repaint.
|
||||
\param Whether to reset the prompt as well. If so
|
||||
|
||||
If reset_cursor is incorreclt set to 0, this may result in screen
|
||||
contents being erased. If it is incorrectly set to one, it may
|
||||
|
@ -189,6 +203,6 @@ void s_write( screen_t *s,
|
|||
resizing, there will be one line of garbage for every repaint,
|
||||
which will quicly fill the screen.
|
||||
*/
|
||||
void s_reset( screen_t *s, bool reset_cursor );
|
||||
void s_reset( screen_t *s, bool reset_cursor, bool reset_prompt = true );
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in a new issue