mirror of
https://github.com/fish-shell/fish-shell
synced 2025-01-13 13:39:02 +00:00
First stab at multiline editing
darcs-hash:20061001160258-ac50b-1a760913e64b96e30ff321d7fbe4069ca161cdfe.gz
This commit is contained in:
parent
2839f5e567
commit
15724d0798
11 changed files with 893 additions and 634 deletions
|
@ -70,7 +70,7 @@ FISH_OBJS := function.o builtin.o complete.o env.o exec.o \
|
|||
expand.o highlight.o history.o kill.o parser.o proc.o reader.o \
|
||||
sanity.o tokenizer.o wildcard.o wgetopt.o wutil.o input.o \
|
||||
output.o intern.o env_universal.o env_universal_common.o \
|
||||
input_common.o event.o signal.o io.o parse_util.o common.o \
|
||||
input_common.o event.o signal.o io.o parse_util.o common.o screen.o\
|
||||
|
||||
# Additional files used by builtin.o
|
||||
BUILTIN_FILES := builtin_help.c builtin_set.c builtin_commandline.c \
|
||||
|
|
5
output.c
5
output.c
|
@ -135,6 +135,11 @@ void output_set_writer( int (*writer)(char) )
|
|||
out = writer;
|
||||
}
|
||||
|
||||
int (*output_get_writer())(char)
|
||||
{
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
void set_color( int c, int c2 )
|
||||
{
|
||||
|
|
4
output.h
4
output.h
|
@ -134,5 +134,9 @@ int writeb( tputs_arg_t b );
|
|||
*/
|
||||
void output_set_writer( int (*writer)(char) );
|
||||
|
||||
//typedef int (*func_ptr_t)(char);
|
||||
|
||||
int (*output_get_writer())(char) ;
|
||||
|
||||
|
||||
#endif
|
||||
|
|
15
parser.c
15
parser.c
|
@ -2996,6 +2996,7 @@ int parser_test( const wchar_t * buff,
|
|||
int previous_pos=current_tokenizer_pos;
|
||||
static int block_pos[BLOCK_MAX_COUNT];
|
||||
static int block_type[BLOCK_MAX_COUNT];
|
||||
int res;
|
||||
|
||||
/*
|
||||
Set to 1 if the current command is inside a pipeline
|
||||
|
@ -3037,7 +3038,7 @@ int parser_test( const wchar_t * buff,
|
|||
current_tokenizer = &tok;
|
||||
|
||||
for( tok_init( &tok, buff, 0 );
|
||||
tok_has_next( &tok );
|
||||
;
|
||||
tok_next( &tok ) )
|
||||
{
|
||||
current_tokenizer_pos = tok_get_pos( &tok );
|
||||
|
@ -3666,6 +3667,8 @@ int parser_test( const wchar_t * buff,
|
|||
|
||||
}
|
||||
|
||||
if( !tok_has_next( &tok ) )
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
|
@ -3709,7 +3712,6 @@ int parser_test( const wchar_t * buff,
|
|||
|
||||
tok_destroy( &tok );
|
||||
|
||||
|
||||
current_tokenizer=previous_tokenizer;
|
||||
current_tokenizer_pos = previous_pos;
|
||||
|
||||
|
@ -3717,6 +3719,13 @@ int parser_test( const wchar_t * buff,
|
|||
|
||||
halloc_free( context );
|
||||
|
||||
return err | ((count!=0)<<1);
|
||||
res = 0;
|
||||
if( err )
|
||||
res |= PARSER_TEST_ERROR;
|
||||
if( count!= 0 )
|
||||
res |= PARSER_TEST_INCOMPLETE;
|
||||
|
||||
return res;
|
||||
|
||||
}
|
||||
|
||||
|
|
3
parser.h
3
parser.h
|
@ -12,6 +12,9 @@
|
|||
#include "parser.h"
|
||||
#include "event.h"
|
||||
|
||||
#define PARSER_TEST_ERROR 1
|
||||
#define PARSER_TEST_INCOMPLETE 2
|
||||
|
||||
/**
|
||||
event_block_t represents a block on events of the specified type
|
||||
*/
|
||||
|
|
622
reader.c
622
reader.c
|
@ -98,6 +98,7 @@ commence.
|
|||
#include "function.h"
|
||||
#include "output.h"
|
||||
#include "signal.h"
|
||||
#include "screen.h"
|
||||
|
||||
#include "parse_util.h"
|
||||
|
||||
|
@ -139,15 +140,7 @@ typedef struct reader_data
|
|||
*/
|
||||
wchar_t *buff;
|
||||
|
||||
/**
|
||||
The output string, may be different than buff if buff can't fit on one line.
|
||||
*/
|
||||
wchar_t *output;
|
||||
|
||||
/**
|
||||
The number of characters used by the prompt
|
||||
*/
|
||||
int prompt_width;
|
||||
screen_t screen;
|
||||
|
||||
/**
|
||||
Buffer containing the current search item
|
||||
|
@ -191,19 +184,17 @@ typedef struct reader_data
|
|||
*/
|
||||
size_t buff_pos;
|
||||
|
||||
/**
|
||||
The current position of the cursor in output buffer.
|
||||
*/
|
||||
size_t output_pos;
|
||||
|
||||
/**
|
||||
Name of the current application
|
||||
*/
|
||||
wchar_t *name;
|
||||
|
||||
/** The prompt text */
|
||||
/** The prompt command */
|
||||
wchar_t *prompt;
|
||||
|
||||
/** The output of the last evaluation of the prompt command */
|
||||
string_buffer_t prompt_buff;
|
||||
|
||||
/**
|
||||
Color is the syntax highlighting for buff. The format is that
|
||||
color[i] is the classification (according to the enum in
|
||||
|
@ -216,11 +207,6 @@ typedef struct reader_data
|
|||
*/
|
||||
int *new_color;
|
||||
|
||||
/**
|
||||
Color for the actual output string.
|
||||
*/
|
||||
int *output_color;
|
||||
|
||||
/**
|
||||
Should the prompt command be reexecuted on the next repaint
|
||||
*/
|
||||
|
@ -249,6 +235,7 @@ typedef struct reader_data
|
|||
When this is true, the reader will exit
|
||||
*/
|
||||
int end_loop;
|
||||
|
||||
/**
|
||||
If this is true, exit reader even if there are running
|
||||
jobs. This happens if we press e.g. ^D twice.
|
||||
|
@ -451,15 +438,12 @@ static int check_size()
|
|||
sizeof(wchar_t)*data->buff_sz);
|
||||
data->search_buff = realloc( data->search_buff,
|
||||
sizeof(wchar_t)*data->buff_sz);
|
||||
data->output = realloc( data->output,
|
||||
sizeof(wchar_t)*data->buff_sz);
|
||||
|
||||
data->color = realloc( data->color,
|
||||
sizeof(int)*data->buff_sz);
|
||||
|
||||
data->new_color = realloc( data->new_color,
|
||||
sizeof(int)*data->buff_sz);
|
||||
data->output_color = realloc( data->output_color,
|
||||
sizeof(int)*data->buff_sz);
|
||||
|
||||
if( data->buff==0 ||
|
||||
data->search_buff==0 ||
|
||||
|
@ -467,111 +451,11 @@ static int check_size()
|
|||
data->new_color == 0 )
|
||||
{
|
||||
DIE_MEM();
|
||||
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
Check if the screen is not wide enough for the buffer, which means
|
||||
the buffer must be scrolled on input and cursor movement.
|
||||
*/
|
||||
static int force_repaint()
|
||||
{
|
||||
int max_width = common_get_width() - data->prompt_width;
|
||||
int pref_width = my_wcswidth( data->buff ) + (data->buff_pos==data->buff_len);
|
||||
return pref_width >= max_width;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Calculate what part of the buffer should be visible
|
||||
|
||||
\return returns 1 screen needs repainting, 0 otherwise
|
||||
*/
|
||||
static int calc_output()
|
||||
{
|
||||
int max_width = common_get_width() - data->prompt_width;
|
||||
int pref_width = my_wcswidth( data->buff ) + (data->buff_pos==data->buff_len);
|
||||
if( pref_width <= max_width )
|
||||
{
|
||||
wcscpy( data->output, data->buff );
|
||||
memcpy( data->output_color, data->color, sizeof(int) * data->buff_len );
|
||||
data->output_pos=data->buff_pos;
|
||||
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
int offset = data->buff_pos;
|
||||
int offset_end = data->buff_pos;
|
||||
int w = 0;
|
||||
wchar_t *pos=data->output;
|
||||
*pos=0;
|
||||
|
||||
|
||||
w = (data->buff_pos==data->buff_len)?1:wcwidth( data->buff[offset] );
|
||||
while( 1 )
|
||||
{
|
||||
int inc=0;
|
||||
int ellipsis_width;
|
||||
|
||||
ellipsis_width = wcwidth(ellipsis_char)*((offset?1:0)+(offset_end<data->buff_len?1:0));
|
||||
|
||||
if( offset > 0 && (ellipsis_width + w + wcwidth( data->buff[offset-1] ) <= max_width ) )
|
||||
{
|
||||
inc=1;
|
||||
offset--;
|
||||
w+= wcwidth( data->buff[offset]);
|
||||
}
|
||||
|
||||
ellipsis_width = wcwidth(ellipsis_char)*((offset?1:0)+(offset_end<data->buff_len?1:0));
|
||||
|
||||
if( offset_end < data->buff_len && (ellipsis_width + w + wcwidth( data->buff[offset_end+1] ) <= max_width ) )
|
||||
{
|
||||
inc = 1;
|
||||
offset_end++;
|
||||
w+= wcwidth( data->buff[offset_end]);
|
||||
}
|
||||
|
||||
if( !inc )
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
data->output_pos = data->buff_pos - offset + (offset?1:0);
|
||||
|
||||
if( offset )
|
||||
{
|
||||
data->output[0]=ellipsis_char;
|
||||
data->output[1]=0;
|
||||
|
||||
}
|
||||
|
||||
wcsncat( data->output,
|
||||
data->buff+offset,
|
||||
offset_end-offset );
|
||||
|
||||
if( offset_end<data->buff_len )
|
||||
{
|
||||
int l = wcslen(data->output);
|
||||
|
||||
data->output[l]=ellipsis_char;
|
||||
data->output[l+1]=0;
|
||||
|
||||
}
|
||||
|
||||
*data->output_color=HIGHLIGHT_NORMAL;
|
||||
|
||||
memcpy( data->output_color+(offset?1:0),
|
||||
data->color+offset,
|
||||
sizeof(int) * (data->buff_len-offset) );
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Compare two completions, ignoring their description.
|
||||
*/
|
||||
|
@ -624,6 +508,7 @@ static void remove_duplicates( array_list_t *l )
|
|||
Translate a highlighting code ()Such as as returned by the highlight function
|
||||
into a color code which is then passed on to set_color.
|
||||
*/
|
||||
|
||||
static void set_color_translated( int c )
|
||||
{
|
||||
set_color( highlight_get_color( c & 0xffff ),
|
||||
|
@ -685,162 +570,15 @@ void reader_write_title()
|
|||
set_color( FISH_COLOR_RESET, FISH_COLOR_RESET );
|
||||
}
|
||||
|
||||
/**
|
||||
Tests if the specified narrow character sequence is present at the
|
||||
specified position of the specified wide character string. All of
|
||||
\c seq must match, but str may be longer than seq.
|
||||
*/
|
||||
static int try_sequence( char *seq, wchar_t *str )
|
||||
{
|
||||
int i;
|
||||
|
||||
for( i=0;; i++ )
|
||||
{
|
||||
if( !seq[i] )
|
||||
return i;
|
||||
|
||||
if( seq[i] != str[i] )
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
Calculate the width of the specified prompt. Does some clever magic
|
||||
to detect common escape sequences that may be embeded in a prompt,
|
||||
such as color codes.
|
||||
*/
|
||||
static int calc_prompt_width( array_list_t *arr )
|
||||
{
|
||||
int res = 0;
|
||||
int i, j, k;
|
||||
|
||||
for( i=0; i<al_get_count( arr ); i++ )
|
||||
{
|
||||
wchar_t *next = (wchar_t *)al_get( arr, i );
|
||||
|
||||
for( j=0; next[j]; j++ )
|
||||
{
|
||||
if( next[j] == L'\e' )
|
||||
{
|
||||
/*
|
||||
This is the start of an escape code. Try to guess it's width.
|
||||
*/
|
||||
int l;
|
||||
int len=0;
|
||||
int found = 0;
|
||||
|
||||
/*
|
||||
Detect these terminfo color escapes with parameter
|
||||
value 0..7, all of which don't move the cursor
|
||||
*/
|
||||
char * esc[] =
|
||||
{
|
||||
set_a_foreground,
|
||||
set_a_background,
|
||||
set_foreground,
|
||||
set_background,
|
||||
}
|
||||
;
|
||||
|
||||
/*
|
||||
Detect these semi-common terminfo escapes without any
|
||||
parameter values, all of which don't move the cursor
|
||||
*/
|
||||
char *esc2[] =
|
||||
{
|
||||
enter_bold_mode,
|
||||
exit_attribute_mode,
|
||||
enter_underline_mode,
|
||||
exit_underline_mode,
|
||||
enter_standout_mode,
|
||||
exit_standout_mode,
|
||||
flash_screen,
|
||||
enter_subscript_mode,
|
||||
exit_subscript_mode,
|
||||
enter_superscript_mode,
|
||||
exit_superscript_mode,
|
||||
enter_blink_mode,
|
||||
enter_italics_mode,
|
||||
exit_italics_mode,
|
||||
enter_reverse_mode,
|
||||
enter_shadow_mode,
|
||||
exit_shadow_mode,
|
||||
enter_standout_mode,
|
||||
exit_standout_mode,
|
||||
enter_secure_mode
|
||||
}
|
||||
;
|
||||
|
||||
for( l=0; l < (sizeof(esc)/sizeof(char *)) && !found; l++ )
|
||||
{
|
||||
if( !esc[l] )
|
||||
continue;
|
||||
|
||||
for( k=0; k<8; k++ )
|
||||
{
|
||||
len = try_sequence( tparm(esc[l],k), &next[j] );
|
||||
if( len )
|
||||
{
|
||||
j += (len-1);
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for( l=0; l < (sizeof(esc2)/sizeof(char *)) && !found; l++ )
|
||||
{
|
||||
if( !esc2[l] )
|
||||
continue;
|
||||
/*
|
||||
Test both padded and unpadded version, just to
|
||||
be safe. Most versions of tparm don't actually
|
||||
seem to do anything these days.
|
||||
*/
|
||||
len = maxi( try_sequence( tparm(esc2[l]), &next[j] ),
|
||||
try_sequence( esc2[l], &next[j] ));
|
||||
|
||||
if( len )
|
||||
{
|
||||
j += (len-1);
|
||||
found = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if( next[j] == L'\t' )
|
||||
{
|
||||
/*
|
||||
Assume tab stops every 8 characters if undefined
|
||||
*/
|
||||
if( init_tabs <= 0 )
|
||||
init_tabs = 8;
|
||||
|
||||
res=( (res/init_tabs)+1 )*init_tabs;
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
Ordinary decent character. Just add width.
|
||||
*/
|
||||
res += wcwidth( next[j] );
|
||||
}
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Write the prompt to screen. If data->exec_prompt is set, the prompt
|
||||
command is first evaluated, and the title will be reexecuted as
|
||||
well.
|
||||
*/
|
||||
static void write_prompt()
|
||||
static void calc_prompt()
|
||||
{
|
||||
int i;
|
||||
set_color( FISH_COLOR_NORMAL, FISH_COLOR_NORMAL );
|
||||
|
||||
/*
|
||||
Check if we need to reexecute the prompt command
|
||||
|
@ -865,21 +603,17 @@ static void write_prompt()
|
|||
proc_pop_interactive();
|
||||
}
|
||||
|
||||
data->prompt_width=calc_prompt_width( &prompt_list );
|
||||
|
||||
data->exec_prompt = 0;
|
||||
reader_write_title();
|
||||
}
|
||||
|
||||
/*
|
||||
Write out the prompt strings
|
||||
*/
|
||||
sb_clear( &data->prompt_buff );
|
||||
|
||||
for( i=0; i<al_get_count( &prompt_list); i++ )
|
||||
{
|
||||
sb_append( &data->prompt_buff, (wchar_t *)al_get( &prompt_list, i ) );
|
||||
}
|
||||
|
||||
for( i=0; i<al_get_count( &prompt_list); i++ )
|
||||
{
|
||||
writestr( (wchar_t *)al_get( &prompt_list, i ) );
|
||||
}
|
||||
set_color( FISH_COLOR_RESET, FISH_COLOR_RESET );
|
||||
|
||||
}
|
||||
|
||||
|
@ -887,7 +621,7 @@ static void write_prompt()
|
|||
Write the whole command line (but not the prompt) to the screen. Do
|
||||
not set the cursor correctly afterwards.
|
||||
*/
|
||||
static void write_cmdline()
|
||||
/*static void write_cmdline()
|
||||
{
|
||||
int i;
|
||||
|
||||
|
@ -897,7 +631,7 @@ static void write_cmdline()
|
|||
writech( data->output[i] );
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
void reader_init()
|
||||
{
|
||||
|
@ -942,47 +676,20 @@ void reader_exit( int do_exit, int forced )
|
|||
|
||||
void repaint( int skip_return )
|
||||
{
|
||||
int steps;
|
||||
int flags = 0;
|
||||
|
||||
calc_output();
|
||||
set_color( FISH_COLOR_RESET, FISH_COLOR_RESET );
|
||||
if( skip_return )
|
||||
flags |= SCREEN_SKIP_RETURN;
|
||||
|
||||
if( !skip_return )
|
||||
writech('\r');
|
||||
calc_prompt();
|
||||
|
||||
writembs(clr_eol);
|
||||
write_prompt();
|
||||
write_cmdline();
|
||||
// assert( wcslen( (wchar_t *)data->prompt_buff.buff));
|
||||
|
||||
/*
|
||||
fwprintf( stderr, L"Width of \'%ls\' (length is %d): ",
|
||||
&data->buff[data->buff_pos],
|
||||
wcslen(&data->buff[data->buff_pos]));
|
||||
fwprintf( stderr, L"%d\n", my_wcswidth(&data->buff[data->buff_pos]));
|
||||
*/
|
||||
s_write( &data->screen, (wchar_t *)data->prompt_buff.buff, data->buff, data->color, data->buff_pos, flags );
|
||||
|
||||
steps = my_wcswidth( &data->output[data->output_pos]);
|
||||
if( steps )
|
||||
move_cursor( -steps );
|
||||
|
||||
set_color( FISH_COLOR_NORMAL, FISH_COLOR_IGNORE );
|
||||
reader_save_status();
|
||||
}
|
||||
|
||||
/**
|
||||
Make sure color values are correct, and repaint if they are not.
|
||||
*/
|
||||
static void check_colors()
|
||||
{
|
||||
reader_super_highlight_me_plenty( data->new_color, data->buff_pos, 0 );
|
||||
if( memcmp( data->new_color, data->color, sizeof(int)*data->buff_len )!=0 )
|
||||
{
|
||||
memcpy( data->color, data->new_color, sizeof(int)*data->buff_len );
|
||||
|
||||
repaint( 0 );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Stat stdout and stderr and save result.
|
||||
|
||||
|
@ -1052,7 +759,6 @@ static void reader_check_status()
|
|||
if( changed )
|
||||
{
|
||||
repaint( 0 );
|
||||
set_color( FISH_COLOR_RESET, FISH_COLOR_RESET );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1062,7 +768,6 @@ static void reader_check_status()
|
|||
*/
|
||||
static void remove_backward()
|
||||
{
|
||||
int wdt;
|
||||
|
||||
if( data->buff_pos <= 0 )
|
||||
return;
|
||||
|
@ -1072,47 +777,17 @@ static void remove_backward()
|
|||
memmove( &data->buff[data->buff_pos-1],
|
||||
&data->buff[data->buff_pos],
|
||||
sizeof(wchar_t)*(data->buff_len-data->buff_pos+1) );
|
||||
|
||||
memmove( &data->color[data->buff_pos-1],
|
||||
&data->color[data->buff_pos],
|
||||
sizeof(wchar_t)*(data->buff_len-data->buff_pos+1) );
|
||||
}
|
||||
data->buff_pos--;
|
||||
data->buff_len--;
|
||||
data->buff[data->buff_len]=0;
|
||||
|
||||
wdt=wcwidth(data->buff[data->buff_pos]);
|
||||
move_cursor(-wdt);
|
||||
data->buff[data->buff_len]='\0';
|
||||
// wcscpy(data->search_buff,data->buff);
|
||||
|
||||
reader_super_highlight_me_plenty( data->new_color,
|
||||
reader_super_highlight_me_plenty( data->color,
|
||||
data->buff_pos,
|
||||
0 );
|
||||
if( (!force_repaint()) && ( memcmp( data->new_color,
|
||||
data->color,
|
||||
sizeof(int)*data->buff_len )==0 ) &&
|
||||
( delete_character != 0) && (wdt==1) )
|
||||
{
|
||||
/*
|
||||
Only do this if delete mode functions, and only for a column
|
||||
wide characters, since terminfo seems to break for other
|
||||
characters. This last check should be removed when terminfo
|
||||
is fixed.
|
||||
*/
|
||||
if( enter_delete_mode != 0 )
|
||||
writembs(enter_delete_mode);
|
||||
writembs(delete_character);
|
||||
if( exit_delete_mode != 0 )
|
||||
writembs(exit_delete_mode);
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy( data->color,
|
||||
data->new_color,
|
||||
sizeof(int) * data->buff_len );
|
||||
|
||||
repaint( 0 );
|
||||
}
|
||||
repaint( 0 );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1124,9 +799,7 @@ static void remove_forward()
|
|||
if( data->buff_pos >= data->buff_len )
|
||||
return;
|
||||
|
||||
move_cursor(wcwidth(data->buff[data->buff_pos]));
|
||||
data->buff_pos++;
|
||||
|
||||
remove_backward();
|
||||
}
|
||||
|
||||
|
@ -1146,10 +819,6 @@ static int insert_char( int c )
|
|||
memmove( &data->buff[data->buff_pos+1],
|
||||
&data->buff[data->buff_pos],
|
||||
sizeof(wchar_t)*(data->buff_len-data->buff_pos) );
|
||||
|
||||
memmove( &data->color[data->buff_pos+1],
|
||||
&data->color[data->buff_pos],
|
||||
sizeof(int)*(data->buff_len-data->buff_pos) );
|
||||
}
|
||||
/* Set character */
|
||||
data->buff[data->buff_pos]=c;
|
||||
|
@ -1161,48 +830,12 @@ static int insert_char( int c )
|
|||
|
||||
/* Syntax highlight */
|
||||
|
||||
reader_super_highlight_me_plenty( data->new_color,
|
||||
reader_super_highlight_me_plenty( data->color,
|
||||
data->buff_pos-1,
|
||||
0 );
|
||||
data->color[data->buff_pos-1] = data->new_color[data->buff_pos-1];
|
||||
|
||||
/* Check if the coloring has changed */
|
||||
if( (!force_repaint()) && ( memcmp( data->new_color,
|
||||
data->color,
|
||||
sizeof(int)*data->buff_len )==0 ) &&
|
||||
( insert_character ||
|
||||
( data->buff_pos == data->buff_len ) ||
|
||||
enter_insert_mode) )
|
||||
{
|
||||
/*
|
||||
Colors look ok, so we set the right color and insert a
|
||||
character
|
||||
*/
|
||||
set_color_translated( data->color[data->buff_pos-1] );
|
||||
if( data->buff_pos < data->buff_len )
|
||||
{
|
||||
if( enter_insert_mode != 0 )
|
||||
writembs(enter_insert_mode);
|
||||
else
|
||||
writembs(insert_character);
|
||||
writech(c);
|
||||
if( insert_padding != 0 )
|
||||
writembs(insert_padding);
|
||||
if( exit_insert_mode != 0 )
|
||||
writembs(exit_insert_mode);
|
||||
}
|
||||
else
|
||||
writech(c);
|
||||
set_color( FISH_COLOR_NORMAL, FISH_COLOR_IGNORE );
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Nope, colors are off, so we repaint the entire command line */
|
||||
memcpy( data->color, data->new_color, sizeof(int) * data->buff_len );
|
||||
repaint( 0 );
|
||||
|
||||
repaint( 0 );
|
||||
}
|
||||
// wcscpy(data->search_buff,data->buff);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -1213,42 +846,31 @@ static int insert_char( int c )
|
|||
static int insert_str(wchar_t *str)
|
||||
{
|
||||
int len = wcslen( str );
|
||||
if( len < 4 )
|
||||
int old_len = data->buff_len;
|
||||
|
||||
data->buff_len += len;
|
||||
check_size();
|
||||
|
||||
/* Insert space for extra characters at the right position */
|
||||
if( data->buff_pos < old_len )
|
||||
{
|
||||
while( (*str)!=0 )
|
||||
if(!insert_char( *str++ ))
|
||||
return 0;
|
||||
memmove( &data->buff[data->buff_pos+len],
|
||||
&data->buff[data->buff_pos],
|
||||
sizeof(wchar_t)*(data->buff_len-data->buff_pos) );
|
||||
}
|
||||
else
|
||||
{
|
||||
int old_len = data->buff_len;
|
||||
memmove( &data->buff[data->buff_pos], str, sizeof(wchar_t)*len );
|
||||
data->buff_pos += len;
|
||||
data->buff[data->buff_len]='\0';
|
||||
|
||||
data->buff_len += len;
|
||||
check_size();
|
||||
/* Syntax highlight */
|
||||
|
||||
/* Insert space for extra characters at the right position */
|
||||
if( data->buff_pos < old_len )
|
||||
{
|
||||
memmove( &data->buff[data->buff_pos+len],
|
||||
&data->buff[data->buff_pos],
|
||||
sizeof(wchar_t)*(data->buff_len-data->buff_pos) );
|
||||
}
|
||||
memmove( &data->buff[data->buff_pos], str, sizeof(wchar_t)*len );
|
||||
data->buff_pos += len;
|
||||
data->buff[data->buff_len]='\0';
|
||||
reader_super_highlight_me_plenty( data->color,
|
||||
data->buff_pos-1,
|
||||
0 );
|
||||
|
||||
/* Syntax highlight */
|
||||
/* repaint */
|
||||
|
||||
reader_super_highlight_me_plenty( data->new_color,
|
||||
data->buff_pos-1,
|
||||
0 );
|
||||
memcpy( data->color, data->new_color, sizeof(int) * data->buff_len );
|
||||
|
||||
/* repaint */
|
||||
|
||||
repaint( 0 );
|
||||
|
||||
}
|
||||
repaint( 0 );
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -1501,7 +1123,10 @@ static void run_pager( wchar_t *prefix, int is_quoted, array_list_t *comp )
|
|||
|
||||
for( i=0; i<al_get_count( comp); i++ )
|
||||
{
|
||||
wchar_t *el = escape((wchar_t*)al_get( comp, i ), 0);
|
||||
wchar_t *el = escape((wchar_t*)al_get( comp, i ), 1);
|
||||
|
||||
// debug( 0, L"Escaped '%ls' to '%ls'", al_get( comp, i ), el );
|
||||
|
||||
|
||||
sb_printf( &msg, L"%ls\n", el );
|
||||
free( el );
|
||||
|
@ -2103,24 +1728,7 @@ static void move_word( int dir, int erase )
|
|||
/* move_cursor(end_buff_pos-data->buff_pos);
|
||||
data->buff_pos = end_buff_pos;
|
||||
*/
|
||||
if( end_buff_pos < data->buff_pos )
|
||||
{
|
||||
while( data->buff_pos != end_buff_pos )
|
||||
{
|
||||
data->buff_pos--;
|
||||
move_cursor( -wcwidth(data->buff[data->buff_pos]));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
while( data->buff_pos != end_buff_pos )
|
||||
{
|
||||
move_cursor( wcwidth(data->buff[data->buff_pos]));
|
||||
data->buff_pos++;
|
||||
check_colors();
|
||||
}
|
||||
}
|
||||
|
||||
data->buff_pos = end_buff_pos;
|
||||
repaint( 0 );
|
||||
// check_colors();
|
||||
}
|
||||
|
@ -2204,18 +1812,22 @@ void reader_run_command( const wchar_t *cmd )
|
|||
|
||||
static int shell_test( wchar_t *b )
|
||||
{
|
||||
if( parser_test( b, 0, 0 ) )
|
||||
int res = parser_test( b, 0, 0 );
|
||||
|
||||
if( res & PARSER_TEST_ERROR )
|
||||
{
|
||||
string_buffer_t sb;
|
||||
sb_init( &sb );
|
||||
|
||||
writech( L'\n' );
|
||||
int tmp[1];
|
||||
|
||||
s_write( &data->screen, L"", L"", tmp, 0, 0 );
|
||||
|
||||
parser_test( b, &sb, L"fish" );
|
||||
fwprintf( stderr, L"%ls", sb.buff );
|
||||
sb_destroy( &sb );
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2233,7 +1845,12 @@ void reader_push( wchar_t *name )
|
|||
reader_data_t *n = calloc( 1, sizeof( reader_data_t ) );
|
||||
n->name = wcsdup( name );
|
||||
n->next = data;
|
||||
|
||||
data=n;
|
||||
|
||||
s_init( &data->screen );
|
||||
sb_init( &data->prompt_buff );
|
||||
|
||||
check_size();
|
||||
data->buff[0]=data->search_buff[0]=0;
|
||||
data->exec_prompt=1;
|
||||
|
@ -2271,8 +1888,9 @@ void reader_pop()
|
|||
free( n->color );
|
||||
free( n->new_color );
|
||||
free( n->search_buff );
|
||||
free( n->output );
|
||||
free( n->output_color );
|
||||
|
||||
s_destroy( &n->screen );
|
||||
sb_destroy( &n->prompt_buff );
|
||||
|
||||
/*
|
||||
Clean up after history search
|
||||
|
@ -2355,8 +1973,6 @@ static void reader_super_highlight_me_plenty( int *color, int match_highlight_po
|
|||
}
|
||||
}
|
||||
|
||||
color[data->buff_pos] = 0;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -2380,7 +1996,7 @@ static int read_i()
|
|||
reader_set_highlight_function( &highlight_shell );
|
||||
reader_set_test_function( &shell_test );
|
||||
|
||||
data->prompt_width=60;
|
||||
// data->prompt_width=60;
|
||||
data->prev_end_loop=0;
|
||||
|
||||
while( (!data->end_loop) && (!sanity_check()) )
|
||||
|
@ -2417,7 +2033,7 @@ static int read_i()
|
|||
if( !reader_exit_forced() && !data->prev_end_loop && has_job )
|
||||
{
|
||||
writestr(_( L"There are stopped jobs\n" ));
|
||||
write_prompt();
|
||||
repaint( 0 );
|
||||
data->end_loop = 0;
|
||||
data->prev_end_loop=1;
|
||||
}
|
||||
|
@ -2480,6 +2096,8 @@ wchar_t *reader_readline()
|
|||
|
||||
al_init( &comp );
|
||||
|
||||
s_reset( &data->screen );
|
||||
|
||||
data->exec_prompt=1;
|
||||
|
||||
reader_super_highlight_me_plenty( data->color, data->buff_pos, 0 );
|
||||
|
@ -2584,6 +2202,7 @@ wchar_t *reader_readline()
|
|||
case R_NULL:
|
||||
{
|
||||
data->exec_prompt=1;
|
||||
s_reset( &data->screen );
|
||||
repaint( 0 );
|
||||
break;
|
||||
}
|
||||
|
@ -2726,7 +2345,8 @@ wchar_t *reader_readline()
|
|||
reader_replace_current_token( data->search_buff );
|
||||
}
|
||||
*data->search_buff=0;
|
||||
check_colors();
|
||||
repaint(0);
|
||||
//check_colors();
|
||||
|
||||
}
|
||||
|
||||
|
@ -2761,27 +2381,54 @@ wchar_t *reader_readline()
|
|||
/* Newline, evaluate*/
|
||||
case L'\n':
|
||||
{
|
||||
data->buff[data->buff_len]=L'\0';
|
||||
|
||||
if( !data->test_func( data->buff ) )
|
||||
/*
|
||||
Allow backslash-escaped newlines
|
||||
*/
|
||||
if( data->buff_len && data->buff[data->buff_len-1]==L'\\' )
|
||||
{
|
||||
|
||||
if( wcslen( data->buff ) )
|
||||
{
|
||||
// wcscpy(data->search_buff,L"");
|
||||
history_add( data->buff );
|
||||
}
|
||||
finished=1;
|
||||
data->buff_pos=data->buff_len;
|
||||
check_colors();
|
||||
writestr( L"\n" );
|
||||
insert_char( '\n' );
|
||||
break;
|
||||
}
|
||||
else
|
||||
|
||||
switch( data->test_func( data->buff ) )
|
||||
{
|
||||
writech('\r');
|
||||
writembs(clr_eol);
|
||||
writech('\n');
|
||||
repaint( 0 );
|
||||
|
||||
case 0:
|
||||
{
|
||||
/*
|
||||
Finished commend, execute it
|
||||
*/
|
||||
if( wcslen( data->buff ) )
|
||||
{
|
||||
// wcscpy(data->search_buff,L"");
|
||||
history_add( data->buff );
|
||||
}
|
||||
finished=1;
|
||||
data->buff_pos=data->buff_len;
|
||||
repaint(0);
|
||||
writestr( L"\n" );
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
We are incomplete, continue editing
|
||||
*/
|
||||
case PARSER_TEST_INCOMPLETE:
|
||||
{
|
||||
insert_char( '\n' );
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
Result must be some combination including an error. The error message will already be printed, all we need to do is repaint
|
||||
*/
|
||||
default:
|
||||
{
|
||||
s_reset( &data->screen );
|
||||
repaint( 0 );
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
break;
|
||||
|
@ -2858,15 +2505,7 @@ wchar_t *reader_readline()
|
|||
if( data->buff_pos > 0 )
|
||||
{
|
||||
data->buff_pos--;
|
||||
if( !force_repaint() )
|
||||
{
|
||||
move_cursor( -wcwidth(data->buff[data->buff_pos]));
|
||||
check_colors();
|
||||
}
|
||||
else
|
||||
{
|
||||
repaint( 0 );
|
||||
}
|
||||
repaint( 0 );
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -2876,18 +2515,8 @@ wchar_t *reader_readline()
|
|||
{
|
||||
if( data->buff_pos < data->buff_len )
|
||||
{
|
||||
if( !force_repaint() )
|
||||
{
|
||||
move_cursor( wcwidth(data->buff[data->buff_pos]));
|
||||
data->buff_pos++;
|
||||
check_colors();
|
||||
}
|
||||
else
|
||||
{
|
||||
data->buff_pos++;
|
||||
|
||||
repaint( 0 );
|
||||
}
|
||||
data->buff_pos++;
|
||||
repaint( 0 );
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -2933,6 +2562,7 @@ wchar_t *reader_readline()
|
|||
{
|
||||
if( clear_screen )
|
||||
writembs( clear_screen );
|
||||
s_reset( &data->screen );
|
||||
repaint( 0 );
|
||||
break;
|
||||
}
|
||||
|
|
554
screen.c
Normal file
554
screen.c
Normal file
|
@ -0,0 +1,554 @@
|
|||
#include "config.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <termios.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef HAVE_SYS_TERMIOS_H
|
||||
#include <sys/termios.h>
|
||||
#endif
|
||||
|
||||
#include <unistd.h>
|
||||
#include <wctype.h>
|
||||
|
||||
#if HAVE_NCURSES_H
|
||||
#include <ncurses.h>
|
||||
#else
|
||||
#include <curses.h>
|
||||
#endif
|
||||
|
||||
#if HAVE_TERMIO_H
|
||||
#include <termio.h>
|
||||
#endif
|
||||
|
||||
#if HAVE_TERM_H
|
||||
#include <term.h>
|
||||
#elif HAVE_NCURSES_TERM_H
|
||||
#include <ncurses/term.h>
|
||||
#endif
|
||||
|
||||
#include <wchar.h>
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
|
||||
#include "fallback.h"
|
||||
#include "common.h"
|
||||
#include "util.h"
|
||||
#include "wutil.h"
|
||||
#include "output.h"
|
||||
#include "highlight.h"
|
||||
#include "screen.h"
|
||||
|
||||
static buffer_t *s_writeb_buffer=0;
|
||||
|
||||
/**
|
||||
Tests if the specified narrow character sequence is present at the
|
||||
specified position of the specified wide character string. All of
|
||||
\c seq must match, but str may be longer than seq.
|
||||
*/
|
||||
static int try_sequence( char *seq, wchar_t *str )
|
||||
{
|
||||
int i;
|
||||
|
||||
for( i=0;; i++ )
|
||||
{
|
||||
if( !seq[i] )
|
||||
return i;
|
||||
|
||||
if( seq[i] != str[i] )
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
Calculate the width of the specified prompt. Does some clever magic
|
||||
to detect common escape sequences that may be embeded in a prompt,
|
||||
such as color codes.
|
||||
*/
|
||||
static int calc_prompt_width( wchar_t *prompt )
|
||||
{
|
||||
int res = 0;
|
||||
int j, k;
|
||||
|
||||
for( j=0; prompt[j]; j++ )
|
||||
{
|
||||
if( prompt[j] == L'\e' )
|
||||
{
|
||||
/*
|
||||
This is the start of an escape code. Try to guess it's width.
|
||||
*/
|
||||
int l;
|
||||
int len=0;
|
||||
int found = 0;
|
||||
|
||||
/*
|
||||
Detect these terminfo color escapes with parameter
|
||||
value 0..7, all of which don't move the cursor
|
||||
*/
|
||||
char * esc[] =
|
||||
{
|
||||
set_a_foreground,
|
||||
set_a_background,
|
||||
set_foreground,
|
||||
set_background,
|
||||
}
|
||||
;
|
||||
|
||||
/*
|
||||
Detect these semi-common terminfo escapes without any
|
||||
parameter values, all of which don't move the cursor
|
||||
*/
|
||||
char *esc2[] =
|
||||
{
|
||||
enter_bold_mode,
|
||||
exit_attribute_mode,
|
||||
enter_underline_mode,
|
||||
exit_underline_mode,
|
||||
enter_standout_mode,
|
||||
exit_standout_mode,
|
||||
flash_screen,
|
||||
enter_subscript_mode,
|
||||
exit_subscript_mode,
|
||||
enter_superscript_mode,
|
||||
exit_superscript_mode,
|
||||
enter_blink_mode,
|
||||
enter_italics_mode,
|
||||
exit_italics_mode,
|
||||
enter_reverse_mode,
|
||||
enter_shadow_mode,
|
||||
exit_shadow_mode,
|
||||
enter_standout_mode,
|
||||
exit_standout_mode,
|
||||
enter_secure_mode
|
||||
}
|
||||
;
|
||||
|
||||
for( l=0; l < (sizeof(esc)/sizeof(char *)) && !found; l++ )
|
||||
{
|
||||
if( !esc[l] )
|
||||
continue;
|
||||
|
||||
for( k=0; k<8; k++ )
|
||||
{
|
||||
len = try_sequence( tparm(esc[l],k), &prompt[j] );
|
||||
if( len )
|
||||
{
|
||||
j += (len-1);
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for( l=0; l < (sizeof(esc2)/sizeof(char *)) && !found; l++ )
|
||||
{
|
||||
if( !esc2[l] )
|
||||
continue;
|
||||
/*
|
||||
Test both padded and unpadded version, just to
|
||||
be safe. Most versions of tparm don't actually
|
||||
seem to do anything these days.
|
||||
*/
|
||||
len = maxi( try_sequence( tparm(esc2[l]), &prompt[j] ),
|
||||
try_sequence( esc2[l], &prompt[j] ));
|
||||
|
||||
if( len )
|
||||
{
|
||||
j += (len-1);
|
||||
found = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if( prompt[j] == L'\t' )
|
||||
{
|
||||
/*
|
||||
Assume tab stops every 8 characters if undefined
|
||||
*/
|
||||
if( init_tabs <= 0 )
|
||||
init_tabs = 8;
|
||||
|
||||
res=( (res/init_tabs)+1 )*init_tabs;
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
Ordinary decent character. Just add width.
|
||||
*/
|
||||
res += wcwidth( prompt[j] );
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void free_line( void *l )
|
||||
{
|
||||
line_t *line = (line_t *)l;
|
||||
// debug( 0, L"Free line at %d", l);
|
||||
al_destroy( &line->text );
|
||||
al_destroy( &line->color );
|
||||
free( line );
|
||||
}
|
||||
|
||||
|
||||
static void s_reset_arr( array_list_t *l )
|
||||
{
|
||||
// debug( 0, L"I have %d lines", al_get_count( l ));
|
||||
|
||||
al_foreach( l, &free_line );
|
||||
al_truncate( l, 0 );
|
||||
}
|
||||
|
||||
void s_init( screen_t *s )
|
||||
{
|
||||
memset( s, 0, sizeof(screen_t));
|
||||
sb_init( &s->prompt_buff );
|
||||
}
|
||||
|
||||
|
||||
void s_destroy( screen_t *s )
|
||||
{
|
||||
s_reset_arr( &s->screen );
|
||||
al_destroy( &s->screen );
|
||||
s_reset_arr( &s->output );
|
||||
al_destroy( &s->output );
|
||||
}
|
||||
|
||||
static line_t *s_create_line()
|
||||
{
|
||||
line_t *current = malloc( sizeof( line_t ));
|
||||
al_init( ¤t->text );
|
||||
al_init( ¤t->color );
|
||||
return current;
|
||||
}
|
||||
|
||||
/*
|
||||
Appends a character to the end of the line that the output cursor is on
|
||||
*/
|
||||
static void s_output_append_char( screen_t *s, wchar_t b, int c, int prompt_width )
|
||||
{
|
||||
int line_no = s->output_cursor[1];
|
||||
|
||||
switch( b )
|
||||
{
|
||||
case L'\n':
|
||||
{
|
||||
int i;
|
||||
line_t *current = s_create_line();
|
||||
al_push( &s->output, current );
|
||||
s->output_cursor[1]++;
|
||||
s->output_cursor[0]=0;
|
||||
for( i=0; i < prompt_width; i++ )
|
||||
{
|
||||
s_output_append_char( s, L' ', 0, prompt_width );
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case L'\r':
|
||||
{
|
||||
line_t *current;
|
||||
current = (line_t *)al_get( &s->output, line_no );
|
||||
al_truncate( ¤t->text, 0 );
|
||||
al_truncate( ¤t->color, 0 );
|
||||
s->output_cursor[0]=0;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
line_t *current;
|
||||
current = (line_t *)al_get( &s->output, line_no );
|
||||
|
||||
if( !current )
|
||||
{
|
||||
current = s_create_line();
|
||||
al_push( &s->output, current );
|
||||
}
|
||||
|
||||
al_push_long( ¤t->text, b );
|
||||
al_push_long( ¤t->color, c );
|
||||
s->output_cursor[0]+= wcwidth(b);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static int s_writeb( char c )
|
||||
{
|
||||
b_append( s_writeb_buffer, &c, 1 );
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void s_move( screen_t *s, buffer_t *b, int new_x, int new_y )
|
||||
{
|
||||
int i;
|
||||
int x_steps, y_steps;
|
||||
|
||||
int (*writer_old)(char) = output_get_writer();
|
||||
|
||||
char *str;
|
||||
/*
|
||||
debug( 0, L"move from %d %d to %d %d",
|
||||
s->screen_cursor[0], s->screen_cursor[1],
|
||||
new_x, new_y );
|
||||
*/
|
||||
output_set_writer( &s_writeb );
|
||||
s_writeb_buffer = b;
|
||||
|
||||
y_steps = new_y - s->screen_cursor[1];
|
||||
|
||||
if( y_steps > 0 && (strcmp( cursor_down, "\n")==0))
|
||||
{
|
||||
/*
|
||||
This is very strange - it seems all (most) consoles use a
|
||||
simple newline as the cursor down escape. This will of
|
||||
course move the cursor to the beginning of the line as
|
||||
well. The cursor_up does not have this behaviour...
|
||||
*/
|
||||
s->screen_cursor[0]=0;
|
||||
}
|
||||
|
||||
if( y_steps < 0 )
|
||||
{
|
||||
str = cursor_up;
|
||||
}
|
||||
else
|
||||
{
|
||||
str = cursor_down;
|
||||
|
||||
}
|
||||
|
||||
for( i=0; i<abs(y_steps); i++)
|
||||
{
|
||||
writembs(str);
|
||||
}
|
||||
|
||||
|
||||
x_steps = new_x - s->screen_cursor[0];
|
||||
|
||||
if( x_steps && new_x == 0 )
|
||||
{
|
||||
char c = '\r';
|
||||
b_append( b, &c, 1 );
|
||||
x_steps = 0;
|
||||
// debug( 0, L"return to first" );
|
||||
}
|
||||
|
||||
if( x_steps < 0 ){
|
||||
str = cursor_left;
|
||||
}
|
||||
else
|
||||
{
|
||||
str = cursor_right;
|
||||
}
|
||||
|
||||
for( i=0; i<abs(x_steps); i++)
|
||||
{
|
||||
writembs(str);
|
||||
}
|
||||
|
||||
|
||||
s->screen_cursor[0] = new_x;
|
||||
s->screen_cursor[1] = new_y;
|
||||
|
||||
output_set_writer( writer_old );
|
||||
|
||||
}
|
||||
|
||||
static void s_set_color( screen_t *s, buffer_t *b, int c )
|
||||
{
|
||||
|
||||
int (*writer_old)(char) = output_get_writer();
|
||||
|
||||
output_set_writer( &s_writeb );
|
||||
s_writeb_buffer = b;
|
||||
|
||||
set_color( highlight_get_color( c & 0xffff ),
|
||||
highlight_get_color( (c>>16)&0xffff ) );
|
||||
|
||||
output_set_writer( writer_old );
|
||||
|
||||
}
|
||||
|
||||
static void s_write_char( screen_t *s, buffer_t *b, wchar_t c )
|
||||
{
|
||||
int (*writer_old)(char) = output_get_writer();
|
||||
|
||||
output_set_writer( &s_writeb );
|
||||
s_writeb_buffer = b;
|
||||
s->screen_cursor[0]+=wcwidth( c );
|
||||
|
||||
writech( c );
|
||||
|
||||
output_set_writer( writer_old );
|
||||
}
|
||||
|
||||
static void s_write_mbs( buffer_t *b, char *s )
|
||||
{
|
||||
int (*writer_old)(char) = output_get_writer();
|
||||
|
||||
output_set_writer( &s_writeb );
|
||||
s_writeb_buffer = b;
|
||||
|
||||
writembs( s );
|
||||
|
||||
output_set_writer( writer_old );
|
||||
}
|
||||
|
||||
|
||||
static void s_write_str( buffer_t *b, wchar_t *s )
|
||||
{
|
||||
int (*writer_old)(char) = output_get_writer();
|
||||
|
||||
output_set_writer( &s_writeb );
|
||||
s_writeb_buffer = b;
|
||||
|
||||
writestr( s );
|
||||
|
||||
output_set_writer( writer_old );
|
||||
}
|
||||
|
||||
|
||||
static void s_update( screen_t *scr, wchar_t *prompt )
|
||||
{
|
||||
int i, j;
|
||||
int prompt_width = calc_prompt_width( prompt );
|
||||
|
||||
buffer_t output;
|
||||
b_init( &output );
|
||||
|
||||
if( wcscmp( prompt, (wchar_t *)scr->prompt_buff.buff ) )
|
||||
{
|
||||
s_move( scr, &output, 0, 0 );
|
||||
s_write_str( &output, prompt );
|
||||
sb_clear( &scr->prompt_buff );
|
||||
sb_append( &scr->prompt_buff, prompt );
|
||||
// debug( 0, L"YAY %d", prompt_width );
|
||||
scr->screen_cursor[0] = prompt_width;
|
||||
}
|
||||
|
||||
for( i=0; i< al_get_count( &scr->output ); i++ )
|
||||
{
|
||||
line_t *o_line = (line_t *)al_get( &scr->output, i );
|
||||
line_t *s_line = (line_t *)al_get( &scr->screen, i );
|
||||
|
||||
if( !s_line )
|
||||
{
|
||||
s_line = s_create_line();
|
||||
al_push( &scr->screen, s_line );
|
||||
}
|
||||
for( j=(i==0?prompt_width:0); j<al_get_count( &o_line->text ); j++ )
|
||||
{
|
||||
wchar_t o = (wchar_t)al_get( &o_line->text, j );
|
||||
int o_c = (int)al_get( &o_line->color, j );
|
||||
if( al_get_count( &s_line->text ) == j )
|
||||
{
|
||||
s_move( scr, &output, j, i );
|
||||
s_set_color( scr, &output, o_c );
|
||||
s_write_char( scr, &output, o );
|
||||
al_set_long( &s_line->text, j, o );
|
||||
al_set_long( &s_line->color, j, o_c );
|
||||
}
|
||||
else
|
||||
{
|
||||
wchar_t s = (wchar_t)al_get( &s_line->text, j );
|
||||
int s_c = (int)al_get( &s_line->color, j );
|
||||
if( o != s || o_c != s_c )
|
||||
{
|
||||
s_move( scr, &output, j, i );
|
||||
s_set_color( scr, &output, o_c );
|
||||
s_write_char( scr, &output, o );
|
||||
al_set_long( &s_line->text, j, o );
|
||||
al_set_long( &s_line->color, j, o_c );
|
||||
}
|
||||
}
|
||||
}
|
||||
// debug( 0, L"frum frum %d %d", al_get_count( &o_line->text ), al_get_count( &s_line->text ) );
|
||||
if( al_get_count( &s_line->text ) > al_get_count( &o_line->text ) )
|
||||
{
|
||||
s_move( scr, &output, al_get_count( &o_line->text ), i );
|
||||
s_write_mbs( &output, clr_eol);
|
||||
al_truncate( &s_line->text, al_get_count( &o_line->text ) );
|
||||
// debug( 0, L"YAY DELETE from %d", al_get_count( &o_line->text ) );
|
||||
}
|
||||
|
||||
}
|
||||
for( i=al_get_count( &scr->output ); i< al_get_count( &scr->screen ); i++ )
|
||||
{
|
||||
line_t *s_line = (line_t *)al_get( &scr->screen, i );
|
||||
s_move( scr, &output, 0, i );
|
||||
s_write_mbs( &output, clr_eol);
|
||||
al_truncate( &s_line->text, 0 );
|
||||
}
|
||||
|
||||
s_move( scr, &output, scr->output_cursor[0], scr->output_cursor[1] );
|
||||
|
||||
s_set_color( scr, &output, 0xffffffff);
|
||||
|
||||
if( output.used )
|
||||
{
|
||||
write( 1, output.buff, output.used );
|
||||
}
|
||||
|
||||
b_destroy( &output );
|
||||
|
||||
}
|
||||
|
||||
|
||||
void s_write( screen_t *s, wchar_t *prompt, wchar_t *b, int *c, int cursor, int flags )
|
||||
{
|
||||
int i;
|
||||
int cursor_arr[2];
|
||||
|
||||
int prompt_width = calc_prompt_width( prompt );
|
||||
|
||||
// debug( 0, L"Prompt width is %d", prompt_width );
|
||||
|
||||
s_reset_arr( &s->output );
|
||||
s->output_cursor[0] = s->output_cursor[1] = 0;
|
||||
|
||||
for( i=0; i<prompt_width; i++ )
|
||||
{
|
||||
s_output_append_char( s, L' ', 0, prompt_width );
|
||||
}
|
||||
|
||||
for( i=0; b[i]; i++ )
|
||||
{
|
||||
int col = c[i];
|
||||
|
||||
if( i == cursor )
|
||||
{
|
||||
memcpy(cursor_arr, s->output_cursor, sizeof(int)*2);
|
||||
col = 0;
|
||||
}
|
||||
|
||||
s_output_append_char( s, b[i], col, prompt_width );
|
||||
|
||||
}
|
||||
if( i == cursor )
|
||||
{
|
||||
memcpy(cursor_arr, s->output_cursor, sizeof(int)*2);
|
||||
}
|
||||
|
||||
memcpy( s->output_cursor, cursor_arr, sizeof(int)*2 );
|
||||
s_update( s, prompt );
|
||||
|
||||
}
|
||||
|
||||
void s_reset( screen_t *s )
|
||||
{
|
||||
s_reset_arr( &s->screen );
|
||||
s->screen_cursor[0] = s->screen_cursor[1] = 0;
|
||||
sb_clear( &s->prompt_buff );
|
||||
}
|
||||
|
30
screen.h
Normal file
30
screen.h
Normal file
|
@ -0,0 +1,30 @@
|
|||
#ifndef FISH_SCREEN_H
|
||||
#define FISH_SCREEN_H
|
||||
|
||||
#define SCREEN_REPAINT 1
|
||||
#define SCREEN_SKIP_RETURN 2
|
||||
|
||||
typedef struct
|
||||
{
|
||||
array_list_t output;
|
||||
array_list_t screen;
|
||||
int output_cursor[2];
|
||||
int screen_cursor[2];
|
||||
string_buffer_t prompt_buff;
|
||||
}
|
||||
screen_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
array_list_t text;
|
||||
array_list_t color;
|
||||
}
|
||||
line_t;
|
||||
|
||||
void s_init( screen_t *s );
|
||||
void s_destroy( screen_t *s );
|
||||
|
||||
void s_write( screen_t *s, wchar_t *prompt, wchar_t *b, int *c, int cursor, int flags );
|
||||
void s_reset( screen_t *s );
|
||||
|
||||
#endif
|
227
tokenizer.c
227
tokenizer.c
|
@ -234,132 +234,140 @@ static void read_string( tokenizer *tok )
|
|||
{
|
||||
// debug(1, L"%lc", *tok->buff );
|
||||
|
||||
if( *tok->buff == L'\\' )
|
||||
{
|
||||
tok->buff++;
|
||||
if( *tok->buff == L'\0' )
|
||||
if( *tok->buff == L'\\' )
|
||||
{
|
||||
tok_error( tok, EOL_ERROR );
|
||||
return;
|
||||
}
|
||||
tok->buff++;
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
The modes are as follows:
|
||||
|
||||
0: regular text
|
||||
1: inside of subshell
|
||||
2: inside of array brackets
|
||||
3: inside of array brackets and subshell, like in '$foo[(ech'
|
||||
*/
|
||||
switch( mode )
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
switch( *tok->buff )
|
||||
tok->buff++;
|
||||
if( *tok->buff == L'\0' )
|
||||
{
|
||||
case L'(':
|
||||
{
|
||||
paran_count=1;
|
||||
mode = 1;
|
||||
break;
|
||||
}
|
||||
tok_error( tok, EOL_ERROR );
|
||||
return;
|
||||
}
|
||||
else if( *tok->buff == L'\n' && mode == 0)
|
||||
{
|
||||
tok->buff--;
|
||||
do_loop = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
case L'[':
|
||||
{
|
||||
if( tok->buff != start )
|
||||
mode=2;
|
||||
break;
|
||||
}
|
||||
tok->buff++;
|
||||
continue;
|
||||
}
|
||||
|
||||
case L'\'':
|
||||
case L'"':
|
||||
{
|
||||
|
||||
const wchar_t *end = quote_end( tok->buff );
|
||||
tok->last_quote = *tok->buff;
|
||||
if( end )
|
||||
/*
|
||||
The modes are as follows:
|
||||
|
||||
0: regular text
|
||||
1: inside of subshell
|
||||
2: inside of array brackets
|
||||
3: inside of array brackets and subshell, like in '$foo[(ech'
|
||||
*/
|
||||
switch( mode )
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
switch( *tok->buff )
|
||||
{
|
||||
case L'(':
|
||||
{
|
||||
tok->buff=(wchar_t *)end;
|
||||
paran_count=1;
|
||||
mode = 1;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
tok->buff += wcslen( tok->buff );
|
||||
|
||||
if( (!tok->accept_unfinished) )
|
||||
case L'[':
|
||||
{
|
||||
if( tok->buff != start )
|
||||
mode=2;
|
||||
break;
|
||||
}
|
||||
|
||||
case L'\'':
|
||||
case L'"':
|
||||
{
|
||||
|
||||
const wchar_t *end = quote_end( tok->buff );
|
||||
tok->last_quote = *tok->buff;
|
||||
if( end )
|
||||
{
|
||||
tok_error( tok, EOL_ERROR );
|
||||
return;
|
||||
tok->buff=(wchar_t *)end;
|
||||
}
|
||||
do_loop = 0;
|
||||
else
|
||||
{
|
||||
tok->buff += wcslen( tok->buff );
|
||||
|
||||
if( (!tok->accept_unfinished) )
|
||||
{
|
||||
tok_error( tok, EOL_ERROR );
|
||||
return;
|
||||
}
|
||||
do_loop = 0;
|
||||
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
if( !is_string_char(*(tok->buff)) )
|
||||
default:
|
||||
{
|
||||
do_loop=0;
|
||||
if( !is_string_char(*(tok->buff)) )
|
||||
{
|
||||
do_loop=0;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 3:
|
||||
case 1:
|
||||
switch( *tok->buff )
|
||||
{
|
||||
case L'\'':
|
||||
case L'\"':
|
||||
{
|
||||
const wchar_t *end = quote_end( tok->buff );
|
||||
if( end )
|
||||
{
|
||||
tok->buff=(wchar_t *)end;
|
||||
}
|
||||
else
|
||||
do_loop = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
case L'(':
|
||||
paran_count++;
|
||||
break;
|
||||
case L')':
|
||||
paran_count--;
|
||||
if( paran_count == 0 )
|
||||
{
|
||||
mode--;
|
||||
}
|
||||
break;
|
||||
case L'\0':
|
||||
do_loop = 0;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
switch( *tok->buff )
|
||||
{
|
||||
case L'(':
|
||||
paran_count=1;
|
||||
mode = 3;
|
||||
break;
|
||||
|
||||
case L']':
|
||||
mode=0;
|
||||
break;
|
||||
|
||||
case L'\0':
|
||||
do_loop = 0;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 3:
|
||||
case 1:
|
||||
switch( *tok->buff )
|
||||
{
|
||||
case L'\'':
|
||||
case L'\"':
|
||||
{
|
||||
const wchar_t *end = quote_end( tok->buff );
|
||||
if( end )
|
||||
{
|
||||
tok->buff=(wchar_t *)end;
|
||||
}
|
||||
else
|
||||
do_loop = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
case L'(':
|
||||
paran_count++;
|
||||
break;
|
||||
case L')':
|
||||
paran_count--;
|
||||
if( paran_count == 0 )
|
||||
{
|
||||
mode--;
|
||||
}
|
||||
break;
|
||||
case L'\0':
|
||||
do_loop = 0;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
switch( *tok->buff )
|
||||
{
|
||||
case L'(':
|
||||
paran_count=1;
|
||||
mode = 3;
|
||||
break;
|
||||
|
||||
case L']':
|
||||
mode=0;
|
||||
break;
|
||||
|
||||
case L'\0':
|
||||
do_loop = 0;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -531,6 +539,8 @@ void tok_next( tokenizer *tok )
|
|||
if(( *(tok->buff) == L'\\') &&( *(tok->buff+1) == L'\n') )
|
||||
{
|
||||
tok->buff+=2;
|
||||
tok->last_type = TOK_END;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -594,7 +604,6 @@ void tok_next( tokenizer *tok )
|
|||
|
||||
default:
|
||||
{
|
||||
|
||||
if( iswdigit( *tok->buff ) )
|
||||
{
|
||||
wchar_t *orig = tok->buff;
|
||||
|
|
19
util.c
19
util.c
|
@ -923,15 +923,20 @@ int wcsfilecmp( const wchar_t *a, const wchar_t *b )
|
|||
}
|
||||
|
||||
int res = wcsfilecmp( a+1, b+1 );
|
||||
switch( abs(res) )
|
||||
|
||||
if( abs(res) < 2 )
|
||||
{
|
||||
case 2:
|
||||
return res;
|
||||
default:
|
||||
if( secondary_diff )
|
||||
return secondary_diff>0?1:-1;
|
||||
/*
|
||||
No primary difference in rest of string.
|
||||
Use secondary difference on this element if found.
|
||||
*/
|
||||
if( secondary_diff )
|
||||
{
|
||||
return secondary_diff>0?1:-1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
||||
return res;
|
||||
|
||||
}
|
||||
|
||||
|
|
18
util.h
18
util.h
|
@ -113,10 +113,20 @@ typedef struct array_list
|
|||
Array containing the data
|
||||
*/
|
||||
anything_t *arr;
|
||||
/** Position to append elements at*/
|
||||
int pos;
|
||||
/** Length of array */
|
||||
int size;
|
||||
|
||||
/**
|
||||
Internal cursor position of the array_list_t. This is the
|
||||
position to append elements at. This is also what the
|
||||
array_list_t considers to be its true size, as reported by
|
||||
al_get_count(), etc. Calls to e.g. al_insert will preserve the
|
||||
values of all elements up to pos.
|
||||
*/
|
||||
size_t pos;
|
||||
|
||||
/**
|
||||
Amount of memory allocated in arr, expressed in number of elements.
|
||||
*/
|
||||
size_t size;
|
||||
}
|
||||
array_list_t;
|
||||
|
||||
|
|
Loading…
Reference in a new issue