#include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if HAVE_NCURSES_H #include #else #include #endif #if HAVE_TERMIO_H #include #endif #include #include #include "util.h" #include "wutil.h" #include "common.h" #include "complete.h" #include "output.h" #include "input_common.h" #include "env_universal.h" enum { LINE_UP = R_NULL+1, LINE_DOWN, PAGE_UP, PAGE_DOWN } ; enum { HIGHLIGHT_PAGER_PREFIX, HIGHLIGHT_PAGER_COMPLETION, HIGHLIGHT_PAGER_DESCRIPTION, HIGHLIGHT_PAGER_PROGRESS } ; /** This struct should be continually updated by signals as the term resizes, and as such always contain the correct current size. */ static struct winsize termsize; static struct termios saved_modes; static struct termios pager_modes; static int is_ca_mode = 0; /** The environment variables used to specify the color of different tokens. */ static wchar_t *hightlight_var[] = { L"fish_pager_color_prefix", L"fish_pager_color_completion", L"fish_pager_color_description", L"fish_pager_color_progress" } ; static string_buffer_t out_buff; static FILE *out_file; int get_color( int highlight ) { if( highlight < 0 ) return FISH_COLOR_NORMAL; if( highlight >= (4) ) return FISH_COLOR_NORMAL; wchar_t *val = env_universal_get( hightlight_var[highlight]); if( val == 0 ) return FISH_COLOR_NORMAL; if( val == 0 ) { return FISH_COLOR_NORMAL; } return output_color_code( val ); } int try_sequence( char *seq ) { int j, k; wint_t c=0; for( j=0; seq[j] != '\0' && seq[j] == (c=input_common_readch( j>0 )); j++ ) ; if( seq[j] == '\0' ) { return 1; } else { input_common_unreadch(c); for(k=j-1; k>=0; k--) input_common_unreadch(seq[k]); } return 0; } static wint_t readch() { struct mapping { char *seq; wint_t bnd; } ; struct mapping m[]= { { "\e[A", LINE_UP } , { key_up, LINE_UP } , { "\e[B", LINE_DOWN } , { key_down, LINE_DOWN } , { key_ppage, PAGE_UP } , { key_npage, PAGE_DOWN } , { " ", PAGE_DOWN } , { "\t", PAGE_DOWN } , { 0, 0 } } ; int i; for( i=0; m[i].seq; i++ ) { if( try_sequence(m[i].seq ) ) return m[i].bnd; } return input_common_readch(0); } /** Print the specified part of the completion list, using the specified column offsets and quoting style. \param l The list of completions to print \param cols number of columns to print in \param width An array specifying the width of each column \param row_start The first row to print \param row_stop the row after the last row to print \param prefix The string to print before each completion \param is_quoted Whether to print the completions are in a quoted environment */ static void completion_print( int cols, int *width, int row_start, int row_stop, wchar_t *prefix, int is_quoted, array_list_t *l) { int rows = (al_get_count( l )-1)/cols+1; int i, j; int prefix_width= my_wcswidth(prefix); for( i = row_start; i 80 ) dw = mini( dw, termsize.ws_col/3 ); *pref_width = cw+dw+4; if( dw > termsize.ws_col/3 ) { dw = termsize.ws_col/3; } *min_width=cw+dw+4; *sep= COMPLETE_SEP; return; } else { *pref_width=*min_width= my_wcswidth( str ); return; } } else { int comp_len=0, desc_len=0; int has_description = 0; while( *str != 0 ) { switch( *str ) { case L'\n': case L'\b': case L'\r': case L'\e': case L'\t': case L'\\': case L'&': case L'$': case L' ': case L'#': case L'^': case L'<': case L'>': case L'@': case L'(': case L')': case L'{': case L'}': case L'?': case L'*': case L'|': case L';': case L':': if( has_description ) desc_len++; else comp_len+=2; break; case COMPLETE_SEP: has_description = 1; break; default: if( has_description ) desc_len+= wcwidth(*str); else comp_len+= wcwidth(*str); break; } str++; } if( has_description ) { /* Mangle long descriptions to make formating look nicer */ debug( 3, L"Desc, width = %d %d\n", comp_len, desc_len ); // if( termsize.ws_col > 80 ) // desc_len = mini( desc_len, termsize.ws_col/3 ); *pref_width = comp_len+ desc_len+4;; comp_len = mini( comp_len, maxi(0,termsize.ws_col/3 - 2)); desc_len = mini( desc_len, maxi(0,termsize.ws_col/5 - 4)); *min_width = comp_len+ desc_len+4; return; } else { debug( 3, L"No desc, width = %d\n", comp_len ); *pref_width=*min_width= comp_len; return; } } } /** Try to print the list of completions l with the prefix prefix using cols as the number of columns. Return 1 if the completion list was printed, 0 if the terminal is to narrow for the specified number of columns. Always succeeds if cols is 1. If all the elements do not fit on the screen at once, make the list scrollable using the up, down and space keys to move. The list will exit when any other key is pressed. \param cols the number of columns to try to fit onto the screen \param prefix the character string to prefix each completion with \param is_quoted whether the completions should be quoted \param l the list of completions \return zero if the specified number of columns do not fit, no-zero otherwise */ static int completion_try_print( int cols, wchar_t *prefix, int is_quoted, array_list_t *l ) { /* The calculated preferred width of each column */ int pref_width[32]; /* The calculated minimum width of each column */ int min_width[32]; /* If the list can be printed with this width, width will contain the width of each column */ int *width=pref_width; /* Set to one if the list should be printed at this width */ int print=0; int i, j; int rows = (al_get_count( l )-1)/cols+1; int pref_tot_width=0; int min_tot_width = 0; int prefix_width = my_wcswidth( prefix ); int res=0; /* Skip completions on tiny terminals */ if( termsize.ws_col < 16 ) return 1; memset( pref_width, 0, sizeof(pref_width) ); memset( min_width, 0, sizeof(min_width) ); /* Calculated how wide the list would be */ for( j = 0; j < cols; j++ ) { for( i = 0; i termsize.ws_col ) { pref_width[0] = termsize.ws_col; } width = pref_width; print=1; } else if( pref_tot_width <= termsize.ws_col ) { /* Terminal is wide enough. Print the list! */ width = pref_width; print=1; } else { int next_rows = (al_get_count( l )-1)/(cols-1)+1; /* fwprintf( stderr, L"cols %d, min_tot %d, term %d, rows=%d, nextrows %d, termrows %d, diff %d\n", cols, min_tot_width, termsize.ws_col, rows, next_rows, termsize.ws_row, pref_tot_width-termsize.ws_col ); */ if( min_tot_width < termsize.ws_col && ( ( (rows < termsize.ws_row) && (next_rows >= termsize.ws_row ) ) || ( pref_tot_width-termsize.ws_col< 4 && cols < 3 ) ) ) { /* Terminal almost wide enough, or squeezing makes the whole list fit on-screen */ int tot_width = min_tot_width; width = min_width; while( tot_width < termsize.ws_col ) { for( i=0; (i=10?L" ": L" "), percent ); writestr(msg); set_color( FISH_COLOR_NORMAL, FISH_COLOR_NORMAL ); int c = readch(); switch( c ) { case LINE_UP: { if( pos > 0 ) { pos--; writembs(tparm( cursor_address, 0, 0)); writembs(scroll_reverse); completion_print( cols, width, pos, pos+1, prefix, is_quoted, l ); writembs( tparm( cursor_address, termsize.ws_row-1, 0) ); writembs(clr_eol ); } break; } case LINE_DOWN: { if( pos <= (rows - termsize.ws_row ) ) { pos++; completion_print( cols, width, pos+termsize.ws_row-2, pos+termsize.ws_row-1, prefix, is_quoted, l ); } break; } case PAGE_DOWN: { npos = mini( rows - termsize.ws_row+1, pos + termsize.ws_row-1 ); if( npos != pos ) { pos = npos; completion_print( cols, width, pos, pos+termsize.ws_row-1, prefix, is_quoted, l ); } else { writembs( flash_screen ); } break; } case PAGE_UP: { npos = maxi( 0, pos - termsize.ws_row+1 ); if( npos != pos ) { pos = npos; completion_print( cols, width, pos, pos+termsize.ws_row-1, prefix, is_quoted, l ); } else { writembs( flash_screen ); } break; } case R_NULL: { do_loop=0; res=2; break; } default: { sb_append_char( &out_buff, c ); do_loop = 0; break; } } } writembs(clr_eol); } } return res; } /** Substitute any series of tabs, newlines, etc. with a single space character in completion description */ static void mangle_descriptions( array_list_t *l ) { int i, skip; for( i=0; i0; i-- ) { switch( completion_try_print( i, prefix, is_quoted, &comp ) ) { case 0: break; case 1: i=0; break; case 2: i=7; break; } } al_foreach( &comp, (void(*)(const void *))&free ); al_destroy( &comp ); free(prefix ); fwprintf( out_file, L"%ls", (wchar_t *)out_buff.buff ); if( is_ca_mode ) writembs(exit_ca_mode); } destroy(); }