Beispiel #1
0
/// Try to print the list of completions l with the prefix prefix using cols as the number of
/// columns. Return true if the completion list was printed, false if the terminal is to narrow for
/// the specified number of columns. Always succeeds if cols is 1.
bool pager_t::completion_try_print(size_t cols, const wcstring &prefix, const comp_info_list_t &lst,
                                   page_rendering_t *rendering, size_t suggested_start_row) const {
    // The calculated preferred width of each column.
    int pref_width[PAGER_MAX_COLS] = {0};
    // The calculated minimum width of each column.
    int min_width[PAGER_MAX_COLS] = {0};
    // 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.
    bool print = false;

    // Compute the effective term width and term height, accounting for disclosure.
    size_t term_width = this->available_term_width;
    size_t term_height =
        this->available_term_height - 1 -
        (search_field_shown ? 1 : 0);  // we always subtract 1 to make room for a comment row
    if (!this->fully_disclosed) {
        term_height = mini(term_height, (size_t)PAGER_UNDISCLOSED_MAX_ROWS);
    }

    size_t row_count = divide_round_up(lst.size(), cols);

    // We have more to disclose if we are not fully disclosed and there's more rows than we have in
    // our term height.
    if (!this->fully_disclosed && row_count > term_height) {
        rendering->remaining_to_disclose = row_count - term_height;
    } else {
        rendering->remaining_to_disclose = 0;
    }

    // If we have only one row remaining to disclose, then squelch the comment row. This prevents us
    // from consuming a line to show "...and 1 more row".
    if (!this->fully_disclosed && rendering->remaining_to_disclose == 1) {
        term_height += 1;
        rendering->remaining_to_disclose = 0;
    }

    size_t pref_tot_width = 0;
    size_t min_tot_width = 0;

    // Skip completions on tiny terminals.
    if (term_width < PAGER_MIN_WIDTH) return true;

    // Calculate how wide the list would be.
    for (size_t col = 0; col < cols; col++) {
        for (size_t row = 0; row < row_count; row++) {
            int pref, min;
            const comp_t *c;
            if (lst.size() <= col * row_count + row) continue;

            c = &lst.at(col * row_count + row);
            pref = c->pref_width;
            min = c->min_width;

            if (col != cols - 1) {
                pref += 2;
                min += 2;
            }
            min_width[col] = maxi(min_width[col], min);
            pref_width[col] = maxi(pref_width[col], pref);
        }
        min_tot_width += min_width[col];
        pref_tot_width += pref_width[col];
    }

    // Force fit if one column.
    if (cols == 1) {
        if (pref_tot_width > term_width) {
            pref_width[0] = term_width;
        }
        width = pref_width;
        print = true;
    } else if (pref_tot_width <= term_width) {
        // Terminal is wide enough. Print the list!
        width = pref_width;
        print = true;
    }

    if (print) {
        // Determine the starting and stop row.
        size_t start_row = 0, stop_row = 0;
        if (row_count <= term_height) {
            // Easy, we can show everything.
            start_row = 0;
            stop_row = row_count;
        } else {
            // We can only show part of the full list. Determine which part based on the
            // suggested_start_row.
            assert(row_count > term_height);
            size_t last_starting_row = row_count - term_height;
            start_row = mini(suggested_start_row, last_starting_row);
            stop_row = start_row + term_height;
            assert(start_row >= 0 && start_row <= last_starting_row);
        }

        assert(stop_row >= start_row);
        assert(stop_row <= row_count);
        assert(stop_row - start_row <= term_height);
        completion_print(cols, width, start_row, stop_row, prefix, lst, rendering);

        // Ellipsis helper string. Either empty or containing the ellipsis char.
        const wchar_t ellipsis_string[] = {ellipsis_char == L'\x2026' ? L'\x2026' : L'\0', L'\0'};

        // Add the progress line. It's a "more to disclose" line if necessary, or a row listing if
        // it's scrollable; otherwise ignore it.
        wcstring progress_text;
        if (rendering->remaining_to_disclose == 1) {
            // I don't expect this case to ever happen.
            progress_text = format_string(_(L"%lsand 1 more row"), ellipsis_string);
        } else if (rendering->remaining_to_disclose > 1) {
            progress_text = format_string(_(L"%lsand %lu more rows"), ellipsis_string,
                                          (unsigned long)rendering->remaining_to_disclose);
        } else if (start_row > 0 || stop_row < row_count) {
            // We have a scrollable interface. The +1 here is because we are zero indexed, but want
            // to present things as 1-indexed. We do not add 1 to stop_row or row_count because
            // these are the "past the last value".
            progress_text =
                format_string(_(L"rows %lu to %lu of %lu"), start_row + 1, stop_row, row_count);
        } else if (completion_infos.empty() && !unfiltered_completion_infos.empty()) {
            // Everything is filtered.
            progress_text = _(L"(no matches)");
        }

        if (!progress_text.empty()) {
            line_t &line = rendering->screen_data.add_line();
            print_max(progress_text, highlight_spec_pager_progress |
                                         highlight_make_background(highlight_spec_pager_progress),
                      term_width, true /* has_more */, &line);
        }

        if (search_field_shown) {
            // Add the search field.
            wcstring search_field_text = search_field_line.text;
            // Append spaces to make it at least the required width.
            if (search_field_text.size() < PAGER_SEARCH_FIELD_WIDTH) {
                search_field_text.append(PAGER_SEARCH_FIELD_WIDTH - search_field_text.size(), L' ');
            }
            line_t *search_field = &rendering->screen_data.insert_line_at_index(0);

            // We limit the width to term_width - 1.
            int search_field_written = print_max(SEARCH_FIELD_PROMPT, highlight_spec_normal,
                                                 term_width - 1, false, search_field);
            print_max(search_field_text, highlight_modifier_force_underline,
                      term_width - search_field_written - 1, false, search_field);
        }
    }
    return print;
}
Beispiel #2
0
static int completion_try_print( int cols,
				 wchar_t *prefix,
				 int is_quoted,
				 std::vector<comp_t *> &lst )
{
	/*
	  The calculated preferred width of each column
	*/
	int pref_width[PAGER_MAX_COLS];
	/*
	  The calculated minimum width of each column
	*/
	int min_width[PAGER_MAX_COLS];
	/*
	  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;
	
	long i, j;
	
	int rows = (int)((lst.size()-1)/cols+1);
	
	int pref_tot_width=0;
	int min_tot_width = 0;
	int res=PAGER_RETRY;
	/*
	  Skip completions on tiny terminals
	*/
	
	if( termsize.ws_col < PAGER_MIN_WIDTH )
		return PAGER_DONE;
	
	memset( pref_width, 0, sizeof(pref_width) );
	memset( min_width, 0, sizeof(min_width) );
	
	/* Calculate how wide the list would be */
	for( j = 0; j < cols; j++ )
	{
		for( i = 0; i<rows; i++ )
		{
			int pref,min;
			comp_t *c;
			if( lst.size() <= j*rows + i )
				continue;

			c = lst.at(j*rows + i );
			pref = c->pref_width;
			min = c->min_width;
			
			if( j != cols-1 )
			{
				pref += 2;
				min += 2;
			}
			min_width[j] = maxi( min_width[j],
					     min );
			pref_width[j] = maxi( pref_width[j],
					      pref );
		}
		min_tot_width += min_width[j];
		pref_tot_width += pref_width[j];
	}
	/*
	  Force fit if one column
	*/
	if( cols == 1)
	{
		if( pref_tot_width > 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
	{
		long next_rows = (lst.size()-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.

			  This part of the code is really important. People hate
			  having to scroll through the completion list. In cases
			  where there are a huge number of completions, it can't
			  be helped, but it is not uncommon for the completions to
			  _almost_ fit on one screen. In those cases, it is almost
			  always desirable to 'squeeze' the completions into a
			  single page. 

			  If we are using N columns and can get everything to
			  fit using squeezing, but everything would also fit
			  using N-1 columns, don't try.
			*/

			int tot_width = min_tot_width;
			width = min_width;

			while( tot_width < termsize.ws_col )
			{
				for( i=0; (i<cols) && ( tot_width < termsize.ws_col ); i++ )
				{
					if( width[i] < pref_width[i] )
					{
						width[i]++;
						tot_width++;
					}
				}
			}
			print=1;
		}
	}

	if( print )
	{
		res=PAGER_DONE;
		if( rows < termsize.ws_row )
		{
			/* List fits on screen. Print it and leave */
			if( is_ca_mode )
			{
				is_ca_mode = 0;
				writembs(exit_ca_mode);
			}
			
			completion_print( cols, width, 0, rows, prefix, is_quoted, lst);
			pager_flush();
		}
		else
		{
			int npos, pos = 0;
			int do_loop = 1;

			/*
			  Enter ca_mode, which means that the terminal
			  content will be restored to the current
			  state on exit.
			*/
			if( enter_ca_mode && exit_ca_mode )
			{
				is_ca_mode=1;
				writembs(enter_ca_mode);
			}
			

			completion_print( cols,
					  width,
					  0,
					  termsize.ws_row-1,
					  prefix,
					  is_quoted,
					  lst);
			/*
			  List does not fit on screen. Print one screenfull and
			  leave a scrollable interface
			*/
			while(do_loop)
			{
				set_color( rgb_color_t::black(), get_color(HIGHLIGHT_PAGER_PROGRESS) );
                wcstring msg = format_string(_(L" %d to %d of %d"), pos, pos+termsize.ws_row-1, rows );
				msg.append(L"   \r" );
								
				writestr(msg.c_str());
				set_color( rgb_color_t::normal(), rgb_color_t::normal() );
				pager_flush();
				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,
									  lst );
							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,
									  lst );
						}
						break;
					}

					case PAGE_DOWN:
					{

						npos = mini( (int)(rows - termsize.ws_row+1), (int)(pos + termsize.ws_row-1) );
						if( npos != pos )
						{
							pos = npos;
							completion_print( cols,
									  width,
									  pos,
									  pos+termsize.ws_row-1,
									  prefix,
									  is_quoted,
									  lst );
						}
						else
						{
							if( flash_screen )
								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,
									  lst );
						}
						else
						{
							if( flash_screen )
								writembs( flash_screen );
						}
						break;
					}

					case R_NULL:
					{
						do_loop=0;
						res=PAGER_RESIZE;
						break;
						
					}
					
					default:
					{
						out_buff.push_back( c );
						do_loop = 0;
						break;
					}					
				}
			}
			writembs(clr_eol);
		}
	}
	return res;
}
Beispiel #3
0
bool pager_t::completion_try_print(size_t cols, const wcstring &prefix, const comp_info_list_t &lst, page_rendering_t *rendering, size_t suggested_start_row) const
{
    /*
      The calculated preferred width of each column
    */
    int pref_width[PAGER_MAX_COLS] = {0};
    /*
      The calculated minimum width of each column
    */
    int min_width[PAGER_MAX_COLS] = {0};
    /*
      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 */
    bool print = false;

    /* Compute the effective term width and term height, accounting for disclosure */
    int term_width = this->available_term_width;
    int term_height = this->available_term_height - 1 - (search_field_shown ? 1 : 0); // we always subtract 1 to make room for a comment row
    if (! this->fully_disclosed)
    {
        term_height = mini(term_height, PAGER_UNDISCLOSED_MAX_ROWS);
    }

    size_t row_count = divide_round_up(lst.size(), cols);

    /* We have more to disclose if we are not fully disclosed and there's more rows than we have in our term height */
    if (! this->fully_disclosed && row_count > term_height)
    {
        rendering->remaining_to_disclose = row_count - term_height;
    }
    else
    {
        rendering->remaining_to_disclose = 0;
    }

    int pref_tot_width=0;
    int min_tot_width = 0;

    /* Skip completions on tiny terminals */
    if (term_width < PAGER_MIN_WIDTH)
        return true;

    /* Calculate how wide the list would be */
    for (long col = 0; col < cols; col++)
    {
        for (long row = 0; row<row_count; row++)
        {
            int pref,min;
            const comp_t *c;
            if (lst.size() <= col*row_count + row)
                continue;

            c = &lst.at(col*row_count + row);
            pref = c->pref_width;
            min = c->min_width;

            if (col != cols-1)
            {
                pref += 2;
                min += 2;
            }
            min_width[col] = maxi(min_width[col],
                                  min);
            pref_width[col] = maxi(pref_width[col],
                                   pref);
        }
        min_tot_width += min_width[col];
        pref_tot_width += pref_width[col];
    }
    /*
      Force fit if one column
    */
    if (cols == 1)
    {
        if (pref_tot_width > term_width)
        {
            pref_width[0] = term_width;
        }
        width = pref_width;
        print = true;
    }
    else if (pref_tot_width <= term_width)
    {
        /* Terminal is wide enough. Print the list! */
        width = pref_width;
        print = true;
    }
    else
    {
        long next_rows = (lst.size()-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, term_width,
          rows, next_rows, term_height,
          pref_tot_width-term_width );
        */
        if (min_tot_width < term_width &&
                (((row_count < term_height) && (next_rows >= term_height)) ||
                 (pref_tot_width-term_width< 4 && cols < 3)))
        {
            /*
              Terminal almost wide enough, or squeezing makes the
              whole list fit on-screen.

              This part of the code is really important. People hate
              having to scroll through the completion list. In cases
              where there are a huge number of completions, it can't
              be helped, but it is not uncommon for the completions to
              _almost_ fit on one screen. In those cases, it is almost
              always desirable to 'squeeze' the completions into a
              single page.

              If we are using N columns and can get everything to
              fit using squeezing, but everything would also fit
              using N-1 columns, don't try.
            */

            int tot_width = min_tot_width;
            width = min_width;

            while (tot_width < term_width)
            {
                for (long i=0; (i<cols) && (tot_width < term_width); i++)
                {
                    if (width[i] < pref_width[i])
                    {
                        width[i]++;
                        tot_width++;
                    }
                }
            }
            print = true;
        }
    }

    if (print)
    {
        /* Determine the starting and stop row */
        size_t start_row = 0, stop_row = 0;
        if (row_count <= term_height)
        {
            /* Easy, we can show everything */
            start_row = 0;
            stop_row = row_count;
        }
        else
        {
            /* We can only show part of the full list. Determine which part based on the suggested_start_row */
            assert(row_count > term_height);
            size_t last_starting_row = row_count - term_height;
            start_row = mini(suggested_start_row, last_starting_row);
            stop_row = start_row + term_height;
            assert(start_row >= 0 && start_row <= last_starting_row);
        }

        assert(stop_row >= start_row);
        assert(stop_row <= row_count);
        assert(stop_row - start_row <= term_height);
        completion_print(cols, width, start_row, stop_row, prefix, lst, rendering);

        /* Ellipsis helper string. Either empty or containing the ellipsis char */
        const wchar_t ellipsis_string[] = {ellipsis_char == L'\x2026' ? L'\x2026' : L'\0', L'\0'};

        /* Add the progress line. It's a "more to disclose" line if necessary, or a row listing if it's scrollable; otherwise ignore it */
        wcstring progress_text;
        if (rendering->remaining_to_disclose == 1)
        {
            /* I don't expect this case to ever happen */
            progress_text = format_string(L"%lsand 1 more row", ellipsis_string);
        }
        else if (rendering->remaining_to_disclose > 1)
        {
            progress_text = format_string(L"%lsand %lu more rows", ellipsis_string, (unsigned long)rendering->remaining_to_disclose);
        }
        else if (start_row > 0 || stop_row < row_count)
        {
            /* We have a scrollable interface. The +1 here is because we are zero indexed, but want to present things as 1-indexed. We do not add 1 to stop_row or row_count because these are the "past the last value" */
            progress_text = format_string(L"rows %lu to %lu of %lu", start_row + 1, stop_row, row_count);
        }
        else if (completion_infos.empty() && ! unfiltered_completion_infos.empty())
        {
            /* Everything is filtered */
            progress_text = L"(no matches)";
        }

        if (! progress_text.empty())
        {
            line_t &line = rendering->screen_data.add_line();
            print_max(progress_text.c_str(), highlight_spec_pager_progress | highlight_make_background(highlight_spec_pager_progress), term_width, true /* has_more */, &line);
        }

        if (search_field_shown)
        {
            /* Add the search field */
            wcstring search_field_text = search_field_line.text;
            /* Append spaces to make it at least the required width */
            if (search_field_text.size() < PAGER_SEARCH_FIELD_WIDTH)
            {
                search_field_text.append(PAGER_SEARCH_FIELD_WIDTH - search_field_text.size(), L' ');
            }
            line_t *search_field = &rendering->screen_data.insert_line_at_index(0);

            /* We limit the width to term_width - 1 */
            int search_field_written = print_max(SEARCH_FIELD_PROMPT, highlight_spec_normal, term_width - 1, false, search_field);
            search_field_written += print_max(search_field_text, highlight_modifier_force_underline, term_width - search_field_written - 1, false, search_field);
        }

    }
    return print;
}