Ejemplo n.º 1
0
static bool test_stuff(screen_t *scr)
{
    data_buffer_t output;
    scoped_buffer_t scoped_buffer(&output);

    s_move(scr, &output, 0, 0);
    int screen_width = common_get_width();

    const wchar_t *left = L"left";
    const wchar_t *right = L"right";

    for (size_t idx = 0; idx < 80; idx++)
    {
        output.push_back('A');
    }

    if (! output.empty())
    {
        write_loop(STDOUT_FILENO, &output.at(0), output.size());
        output.clear();
    }

    sleep(5);

    for (size_t i=0; i < 1; i++)
    {
        writembs(cursor_left);
    }

    if (! output.empty())
    {
        write_loop(1, &output.at(0), output.size());
        output.clear();
    }



    while (1)
    {
        int c = getchar();
        if (c != EOF) break;
    }


    while (1)
    {
        int c = getchar();
        if (c != EOF) break;
    }
    puts("Bye");
    exit(0);
    while (1) sleep(10000);
    return true;
}
Ejemplo n.º 2
0
/// Appends a character to the end of the line that the output cursor is on. This function
/// automatically handles linebreaks and lines longer than the screen width.
static void s_desired_append_char(screen_t *s, wchar_t b, highlight_spec_t c, int indent,
                                  size_t prompt_width) {
    int line_no = s->desired.cursor.y;

    if (b == L'\n') {
        // Current line is definitely hard wrapped.
        s->desired.create_line(s->desired.line_count());
        s->desired.line(s->desired.cursor.y).is_soft_wrapped = false;
        s->desired.cursor.y++;
        s->desired.cursor.x = 0;
        for (size_t i = 0; i < prompt_width + indent * INDENT_STEP; i++) {
            s_desired_append_char(s, L' ', highlight_spec_t{}, indent, prompt_width);
        }
    } else if (b == L'\r') {
        line_t &current = s->desired.line(line_no);
        current.clear();
        s->desired.cursor.x = 0;
    } else {
        int screen_width = common_get_width();
        int cw = fish_wcwidth_min_0(b);

        s->desired.create_line(line_no);

        // Check if we are at the end of the line. If so, continue on the next line.
        if ((s->desired.cursor.x + cw) > screen_width) {
            // Current line is soft wrapped (assuming we support it).
            s->desired.line(s->desired.cursor.y).is_soft_wrapped = true;
            // std::fwprintf(stderr, L"\n\n1 Soft wrapping %d\n\n", s->desired.cursor.y);

            line_no = (int)s->desired.line_count();
            s->desired.add_line();
            s->desired.cursor.y++;
            s->desired.cursor.x = 0;
        }

        line_t &line = s->desired.line(line_no);
        line.append(b, c);
        s->desired.cursor.x += cw;

        // Maybe wrap the cursor to the next line, even if the line itself did not wrap. This
        // avoids wonkiness in the last column.
        if (s->desired.cursor.x >= screen_width) {
            line.is_soft_wrapped = true;
            s->desired.cursor.x = 0;
            s->desired.cursor.y++;
        }
    }
}
Ejemplo n.º 3
0
env_var_t env_get_string(const wcstring &key, env_mode_flags_t mode)
{
    const bool has_scope = mode & (ENV_LOCAL | ENV_GLOBAL | ENV_UNIVERSAL);
    const bool search_local = !has_scope || (mode & ENV_LOCAL);
    const bool search_global = !has_scope || (mode & ENV_GLOBAL);
    const bool search_universal = !has_scope || (mode & ENV_UNIVERSAL);

    const bool search_exported = (mode & ENV_EXPORT) || !(mode & ENV_UNEXPORT);
    const bool search_unexported = (mode & ENV_UNEXPORT) || !(mode & ENV_EXPORT);

    /* Make the assumption that electric keys can't be shadowed elsewhere, since we currently block that in env_set() */
    if (is_electric(key))
    {
        if (!search_global) return env_var_t::missing_var();
        /* Big hack...we only allow getting the history on the main thread. Note that history_t may ask for an environment variable, so don't take the lock here (we don't need it) */
        if (key == L"history" && is_main_thread())
        {
            env_var_t result;

            history_t *history = reader_get_history();
            if (! history)
            {
                history = &history_t::history_with_name(L"fish");
            }
            if (history)
                history->get_string_representation(&result, ARRAY_SEP_STR);
            return result;
        }
        else if (key == L"COLUMNS")
        {
            return to_string(common_get_width());
        }
        else if (key == L"LINES")
        {
            return to_string(common_get_height());
        }
        else if (key == L"status")
        {
            return to_string(proc_get_last_status());
        }
        else if (key == L"umask")
        {
            return format_string(L"0%0.3o", get_umask());
        }
        // we should never get here unless the electric var list is out of sync
    }

    if (search_local || search_global) {
        /* Lock around a local region */
        scoped_lock lock(env_lock);

        env_node_t *env = search_local ? top : global_env;

        while (env != NULL)
        {
            const var_entry_t *entry = env->find_entry(key);
            if (entry != NULL && (entry->exportv ? search_exported : search_unexported))
            {
                if (entry->val == ENV_NULL)
                {
                    return env_var_t::missing_var();
                }
                else
                {
                    return entry->val;
                }
            }

            if (has_scope)
            {
                if (!search_global || env == global_env) break;
                env = global_env;
            }
            else
            {
                env = env->next_scope_to_search();
            }
        }
    }

    if (!search_universal) return env_var_t::missing_var();

    /* Another big hack - only do a universal barrier on the main thread (since it can change variable values)
        Make sure we do this outside the env_lock because it may itself call env_get_string */
    if (is_main_thread() && ! get_proc_had_barrier())
    {
        set_proc_had_barrier(true);
        env_universal_barrier();
    }

    if (uvars())
    {
        env_var_t env_var = uvars()->get(key);
        if (env_var == ENV_NULL || !(uvars()->get_export(key) ? search_exported : search_unexported))
        {
            env_var = env_var_t::missing_var();
        }
        return env_var;
    }
    return env_var_t::missing_var();
}
Ejemplo n.º 4
0
env_var_t env_get_string(const wcstring &key)
{
    /* Big hack...we only allow getting the history on the main thread. Note that history_t may ask for an environment variable, so don't take the lock here (we don't need it) */
    const bool is_main = is_main_thread();
    if (key == L"history" && is_main)
    {
        env_var_t result;

        history_t *history = reader_get_history();
        if (! history)
        {
            history = &history_t::history_with_name(L"fish");
        }
        if (history)
            history->get_string_representation(result, ARRAY_SEP_STR);
        return result;
    }
    else if (key == L"COLUMNS")
    {
        return to_string(common_get_width());
    }
    else if (key == L"LINES")
    {
        return to_string(common_get_height());
    }
    else if (key == L"status")
    {
        return to_string(proc_get_last_status());
    }
    else if (key == L"umask")
    {
        return format_string(L"0%0.3o", get_umask());
    }
    else
    {
        {
            /* Lock around a local region */
            scoped_lock lock(env_lock);

            env_node_t *env = top;
            wcstring result;

            while (env != NULL)
            {
                const var_entry_t *entry = env->find_entry(key);
                if (entry != NULL)
                {
                    if (entry->val == ENV_NULL)
                    {
                        return env_var_t::missing_var();
                    }
                    else
                    {
                        return entry->val;
                    }
                }

                env = env->next_scope_to_search();
            }
        }

        /* Another big hack - only do a universal barrier on the main thread (since it can change variable values)
           Make sure we do this outside the env_lock because it may itself call env_get_string */
        if (is_main && ! get_proc_had_barrier())
        {
            set_proc_had_barrier(true);
            env_universal_barrier();
        }

        const wchar_t *item = env_universal_get(key);

        if (!item || (wcscmp(item, ENV_NULL)==0))
        {
            return env_var_t::missing_var();
        }
        else
        {
            return item;
        }
    }
}
Ejemplo n.º 5
0
void s_write(screen_t *s, const wcstring &left_prompt, const wcstring &right_prompt,
             const wcstring &commandline, size_t explicit_len,
             const std::vector<highlight_spec_t> &colors, const std::vector<int> &indent,
             size_t cursor_pos, const page_rendering_t &pager, bool cursor_is_within_pager) {
    screen_data_t::cursor_t cursor_arr;

    // 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(STDOUT_FILENO, "\r", 1);
        write_loop(STDOUT_FILENO, prompt_narrow.c_str(), prompt_narrow.size());
        write_loop(STDOUT_FILENO, 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);

    // Determine whether, if we have an autosuggestion, it was truncated.
    s->autosuggestion_is_truncated =
        !autosuggestion.empty() && autosuggestion != layout.autosuggestion;

    // 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' ', highlight_spec_t{}, 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', highlight_spec_t{}, 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++) {
        // Grab the current cursor's x,y position if this character matches the cursor's offset.
        if (!cursor_is_within_pager && i == cursor_pos) {
            cursor_arr = s->desired.cursor;
        }
        s_desired_append_char(s, effective_commandline.at(i), colors[i], indent[i],
                              first_line_prompt_space);
    }

    // Cursor may have been at the end too.
    if (!cursor_is_within_pager && i == cursor_pos) {
        cursor_arr = s->desired.cursor;
    }

    // Now that we've output everything, set the cursor to the position that we saved in the loop
    // above.
    s->desired.cursor = cursor_arr;

    if (cursor_is_within_pager) {
        s->desired.cursor.x = (int)cursor_pos;
        s->desired.cursor.y = (int)s->desired.line_count();
    }

    // Append pager_data (none if empty).
    s->desired.append_lines(pager.screen_data);

    s_update(s, layout.left_prompt, layout.right_prompt);
    s_save_status(s);
}
Ejemplo n.º 6
0
/// Update the screen to match the desired output.
static void s_update(screen_t *scr, const wcstring &left_prompt, const wcstring &right_prompt) {
    const environment_t &vars = env_stack_t::principal();
    const scoped_buffer_t buffering(*scr);
    const size_t left_prompt_width =
        calc_prompt_layout(left_prompt, cached_layouts).last_line_width;
    const size_t right_prompt_width =
        calc_prompt_layout(right_prompt, cached_layouts).last_line_width;

    int screen_width = common_get_width();

    // Figure out how many following lines we need to clear (probably 0).
    size_t actual_lines_before_reset = scr->actual_lines_before_reset;
    scr->actual_lines_before_reset = 0;

    bool need_clear_lines = scr->need_clear_lines;
    bool need_clear_screen = scr->need_clear_screen;
    bool has_cleared_screen = false;

    if (scr->actual_width != screen_width) {
        // Ensure we don't issue a clear screen for the very first output, to avoid issue #402.
        if (scr->actual_width != SCREEN_WIDTH_UNINITIALIZED) {
            need_clear_screen = true;
            s_move(scr, 0, 0);
            s_reset(scr, screen_reset_current_line_contents);

            need_clear_lines = need_clear_lines || scr->need_clear_lines;
            need_clear_screen = need_clear_screen || scr->need_clear_screen;
        }
        scr->actual_width = screen_width;
    }

    scr->need_clear_lines = false;
    scr->need_clear_screen = false;

    // 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 = std::max(actual_lines_before_reset, scr->actual.line_count());

    if (left_prompt != scr->actual_left_prompt) {
        s_move(scr, 0, 0);
        s_write_str(scr, left_prompt.c_str());
        scr->actual_left_prompt = left_prompt;
        scr->actual.cursor.x = (int)left_prompt_width;
    }

    for (size_t i = 0; i < scr->desired.line_count(); i++) {
        const line_t &o_line = scr->desired.line(i);
        line_t &s_line = scr->actual.create_line(i);
        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_screen && 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;

        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) {
                size_t prefix_width = fish_wcswidth(&o_line.text.at(0), shared_prefix);
                if (prefix_width > skip_remaining) skip_remaining = prefix_width;
            }

            // If we're soft wrapped, and if we're going to change the first character of the next
            // line, don't skip over the last two characters so that we maintain soft-wrapping.
            if (o_line.is_soft_wrapped && i + 1 < scr->desired.line_count()) {
                bool next_line_will_change = true;
                if (i + 1 < scr->actual.line_count()) {  //!OCLINT
                    if (line_shared_prefix(scr->desired.line(i + 1), scr->actual.line(i + 1)) > 0) {
                        next_line_will_change = false;
                    }
                }
                if (next_line_will_change) {
                    skip_remaining = std::min(skip_remaining, (size_t)(scr->actual_width - 2));
                }
            }
        }

        // Skip over skip_remaining width worth of characters.
        size_t j = 0;
        for (; j < o_line.size(); j++) {
            size_t width = fish_wcwidth_min_0(o_line.char_at(j));
            if (skip_remaining < width) break;
            skip_remaining -= width;
            current_width += width;
        }

        // Skip over zero-width characters (e.g. combining marks at the end of the prompt).
        for (; j < o_line.size(); j++) {
            int width = fish_wcwidth_min_0(o_line.char_at(j));
            if (width > 0) break;
        }

        // Now actually output stuff.
        for (; j < o_line.size(); j++) {
            // If we are about to output into the last column, clear the screen first. If we clear
            // the screen after we output into the last column, it can erase the last character due
            // to the sticky right cursor. If we clear the screen too early, we can defeat soft
            // wrapping.
            if (j + 1 == (size_t)screen_width && should_clear_screen_this_line &&
                !has_cleared_screen) {
                s_move(scr, current_width, (int)i);
                s_write_mbs(scr, clr_eos);
                has_cleared_screen = true;
            }

            perform_any_impending_soft_wrap(scr, current_width, (int)i);
            s_move(scr, current_width, (int)i);
            s_set_color(scr, vars, o_line.color_at(j));
            s_write_char(scr, o_line.char_at(j));
            current_width += fish_wcwidth_min_0(o_line.char_at(j));
        }

        // Clear the screen if we have not done so yet.
        if (should_clear_screen_this_line && !has_cleared_screen) {
            s_move(scr, current_width, (int)i);
            s_write_mbs(scr, clr_eos);
            has_cleared_screen = true;
        }

        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 (has_cleared_screen) {
            // Already cleared everything.
            clear_remainder = false;
        } else if (need_clear_lines && current_width < screen_width) {
            clear_remainder = true;
        } else if (right_prompt_width < scr->last_right_prompt_width) {
            clear_remainder = true;
        } else {
            int prev_width =
                s_line.text.empty() ? 0 : fish_wcswidth(&s_line.text.at(0), s_line.text.size());
            clear_remainder = prev_width > current_width;
        }
        if (clear_remainder && clr_eol) {
            s_set_color(scr, vars, highlight_spec_t{});
            s_move(scr, current_width, (int)i);
            s_write_mbs(scr, clr_eol);
        }

        // Output any rprompt if this is the first line.
        if (i == 0 && right_prompt_width > 0) {  //!OCLINT(Use early exit/continue)
            s_move(scr, (int)(screen_width - right_prompt_width), (int)i);
            s_set_color(scr, vars, highlight_spec_t{});
            s_write_str(scr, right_prompt.c_str());
            scr->actual.cursor.x += right_prompt_width;

            // We output in the last column. Some terms (Linux) push the cursor further right, past
            // the window. Others make it "stick." Since we don't really know which is which, issue
            // a cr so it goes back to the left.
            //
            // However, if the user is resizing the window smaller, then it's possible the cursor
            // wrapped. If so, then a cr will go to the beginning of the following line! So instead
            // issue a bunch of "move left" commands to get back onto the line, and then jump to the
            // front of it.
            s_move(scr, scr->actual.cursor.x - (int)right_prompt_width, scr->actual.cursor.y);
            s_write_str(scr, L"\r");
            scr->actual.cursor.x = 0;
        }
    }

    // Clear remaining lines (if any) if we haven't cleared the screen.
    if (!has_cleared_screen && scr->desired.line_count() < lines_with_stuff && clr_eol) {
        s_set_color(scr, vars, highlight_spec_t{});
        for (size_t i = scr->desired.line_count(); i < lines_with_stuff; i++) {
            s_move(scr, 0, (int)i);
            s_write_mbs(scr, clr_eol);
        }
    }

    s_move(scr, scr->desired.cursor.x, scr->desired.cursor.y);
    s_set_color(scr, vars, highlight_spec_t{});

    // We have now synced our actual screen against our desired screen. Note that this is a big
    // assignment!
    scr->actual = scr->desired;
    scr->last_right_prompt_width = right_prompt_width;
}
Ejemplo n.º 7
0
/// Write the bytes needed to move screen cursor to the specified position to the specified buffer.
/// The actual_cursor field of the specified screen_t will be updated.
///
/// \param s the screen to operate on
/// \param b the buffer to send the output escape codes to
/// \param new_x the new x position
/// \param new_y the new y position
static void s_move(screen_t *s, int new_x, int new_y) {
    if (s->actual.cursor.x == new_x && s->actual.cursor.y == new_y) return;

    const scoped_buffer_t buffering(*s);

    // If we are at the end of our window, then either the cursor stuck to the edge or it didn't. We
    // don't know! We can fix it up though.
    if (s->actual.cursor.x == common_get_width()) {
        // Either issue a cr to go back to the beginning of this line, or a nl to go to the
        // beginning of the next one, depending on what we think is more efficient.
        if (new_y <= s->actual.cursor.y) {
            s->outp().push_back('\r');
        } else {
            s->outp().push_back('\n');
            s->actual.cursor.y++;
        }
        // Either way we're not in the first column.
        s->actual.cursor.x = 0;
    }

    int i;
    int x_steps, y_steps;

    const char *str;
    auto &outp = s->outp();

    y_steps = new_y - s->actual.cursor.y;

    if (y_steps > 0 && (std::strcmp(cursor_down, "\n") == 0)) {
        // This is very strange - it seems some (all?) 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 as
        // moving it down one step. The cursor_up does not have this behavior...
        s->actual.cursor.x = 0;
    }

    if (y_steps < 0) {
        str = cursor_up;
    } else {
        str = cursor_down;
    }

    for (i = 0; i < abs(y_steps); i++) {
        writembs(outp, str);
    }

    x_steps = new_x - s->actual.cursor.x;

    if (x_steps && new_x == 0) {
        outp.push_back('\r');
        x_steps = 0;
    }

    const char *multi_str = NULL;
    if (x_steps < 0) {
        str = cursor_left;
        multi_str = parm_left_cursor;
    } else {
        str = cursor_right;
        multi_str = parm_right_cursor;
    }

    // Use the bulk ('multi') output for cursor movement if it is supported and it would be shorter
    // Note that this is required to avoid some visual glitches in iTerm (issue #1448).
    bool use_multi =
        multi_str != NULL && multi_str[0] != '\0' && abs(x_steps) * std::strlen(str) > std::strlen(multi_str);
    if (use_multi && cur_term) {
        char *multi_param = tparm((char *)multi_str, abs(x_steps));
        writembs(outp, multi_param);
    } else {
        for (i = 0; i < abs(x_steps); i++) {
            writembs(outp, str);
        }
    }

    s->actual.cursor.x = new_x;
    s->actual.cursor.y = new_y;
}
Ejemplo n.º 8
0
void s_write( screen_t *s,
	      const wchar_t *prompt,
	      const wchar_t *commandline, 
	      size_t explicit_len,
	      const int *c, 
	      const int *indent,
	      size_t cursor_pos )
{
	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( prompt, );
	CHECK( commandline, );
	CHECK( c, );
	CHECK( indent, );

	/*
	  If we are using a dumb terminal, don't try any fancy stuff,
	  just print out the text.
	 */
	if( is_dumb() )
	{
		char *prompt_narrow = wcs2str( prompt );
		char *buffer_narrow = wcs2str( commandline );
		
		write_loop( 1, "\r", 1 );
		write_loop( 1, prompt_narrow, strlen( prompt_narrow ) );
		write_loop( 1, buffer_narrow, strlen( buffer_narrow ) );

		free( prompt_narrow );
		free( buffer_narrow );
		
		return;
	}
	
	prompt_width = calc_prompt_width( prompt );
	screen_width = common_get_width();

	s_check_status( s );

	/*
	  Ignore prompts wider than the screen - only print a two
	  character placeholder...

	  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 )
	{
		prompt = L"> ";
		prompt_width = 2;
	}
	
	/*
	  Completely ignore impossibly small screens
	*/
	if( screen_width < 4 )
	{
		return;
	}

	/*
	  Check if we are overflowing
	 */
    size_t last_char_that_fits = 0;
	for( size_t i=0; commandline[i]; i++ )
	{
		if( commandline[i] == L'\n' )
		{
			if( current_line_width > max_line_width )
				max_line_width = current_line_width;
			current_line_width = indent[i]*INDENT_STEP;
            newline_count++;
		}
		else
		{
            int width = fish_wcwidth(commandline[i]);
			current_line_width += width;
            if (i < explicit_len)
                explicit_portion_width += width;
                
            if (prompt_width + current_line_width < screen_width)
                last_char_that_fits = i;
		}
	}
	if( current_line_width > max_line_width )
		max_line_width = current_line_width;

    s->desired.resize(0);
	s->desired.cursor.x = s->desired.cursor.y = 0;
    
    /* 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)
    {
        assert(screen_width - prompt_width >= 1);
        max_line_width = screen_width - prompt_width - 1;
        truncated_autosuggestion_line = wcstring(commandline, 0, last_char_that_fits);
        commandline = truncated_autosuggestion_line.c_str();
    }
	for( size_t i=0; i<prompt_width; i++ )
	{
		s_desired_append_char( s, L' ', 0, 0, prompt_width );
	}

	/*
	  If overflowing, give the prompt its own line to improve the
	  situation.
	 */
	if( max_line_width + prompt_width >= screen_width )
	{
		s_desired_append_char( s, L'\n', 0, 0, 0 );
		prompt_width=0;
	}
	
    size_t i;
	for( i=0; commandline[i]; i++ )
	{
		int col = c[i];
		
		if( i == cursor_pos )
		{
			col = 0;
		}
		
		if( i == cursor_pos )
		{
            cursor_arr = s->desired.cursor;
		}
		
		s_desired_append_char( s, commandline[i], col, indent[i], prompt_width );
		
	}
	if( i == cursor_pos )
	{
        cursor_arr = s->desired.cursor;
	}
	
    s->desired.cursor = cursor_arr;
	s_update( s, prompt );
	s_save_status( s );
}
Ejemplo n.º 9
0
/**
   Update the screen to match the desired output.
*/
static void s_update( screen_t *scr, const wchar_t *prompt )
{
	size_t prompt_width = calc_prompt_width( prompt );
	int screen_width = common_get_width();
	int need_clear = scr->need_clear;
	data_buffer_t output;

	scr->need_clear = 0;
		
	if( scr->actual_width != screen_width )
	{
		need_clear = 1;
		s_move( scr, &output, 0, 0 );
		scr->actual_width = screen_width;
		s_reset( scr, false );
	}
	
	if( wcscmp( prompt, scr->actual_prompt.c_str() ) )
	{
		s_move( scr, &output, 0, 0 );
		s_write_str( &output, prompt );
        scr->actual_prompt = prompt;
		scr->actual.cursor.x = (int)prompt_width;
	}
	
    for (size_t i=0; i < scr->desired.line_count(); i++)
	{
		const line_t &o_line = scr->desired.line(i);
		line_t &s_line = scr->actual.create_line(i);
		size_t start_pos = (i==0 ? prompt_width : 0);
        int current_width = 0;
        
		if( need_clear )
		{
			s_move( scr, &output, (int)start_pos, (int)i );
			s_write_mbs( &output, clr_eol);
            s_line.clear();
		}
        
        /* 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);
        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;
        }
        
        /* Skip over skip_remaining width worth of characters */
        size_t j = 0;
        for ( ; j < o_line.size(); j++)
        {
            int width = fish_wcwidth(o_line.char_at(j));
            if (skip_remaining <= width)
                break;
            skip_remaining -= width;
            current_width += width;
        }
        
        /* Skip over zero-width characters (e.g. combining marks at the end of the prompt) */
        for ( ; j < o_line.size(); j++)
        {
            int width = fish_wcwidth(o_line.char_at(j));
            if (width > 0)
                break;
        }
        
        /* Now actually output stuff */
        for ( ; j < o_line.size(); j++)
        {
            s_move( scr, &output, current_width, (int)i );
            s_set_color( scr, &output, o_line.color_at(j) );
            s_write_char( scr, &output, o_line.char_at(j) );
            current_width += fish_wcwidth(o_line.char_at(j));
        }
        
        /* If we wrote more on this line last time, clear it */
        int prev_length = (s_line.text.empty() ? 0 : fish_wcswidth(&s_line.text.at(0), s_line.text.size()));
        if (prev_length > current_width )
		{
			s_move( scr, &output, current_width, (int)i );
			s_write_mbs( &output, clr_eol);
		}
	}
    
    /* Clear remaining lines */
    for( size_t i=scr->desired.line_count(); i < scr->actual.line_count(); 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);

	if( ! output.empty() )
	{
		write_loop( 1, &output.at(0), output.size() );
	}
	
    /* We have now synced our actual screen against our desired screen. Note that this is a big assignment! */
    scr->actual = scr->desired;
}
Ejemplo n.º 10
0
/**
   Appends a character to the end of the line that the output cursor is
   on. This function automatically handles linebreaks and lines longer
   than the screen width. 
*/
static void s_desired_append_char( screen_t *s,
                                  wchar_t b,
                                  int c,
                                  int indent,
                                  size_t prompt_width )
{
	int line_no = s->desired.cursor.y;
	
	switch( b )
	{
		case L'\n':
		{
			int i;
            s->desired.create_line(s->desired.line_count());
			s->desired.cursor.y++;
			s->desired.cursor.x=0;
			for( i=0; i < prompt_width+indent*INDENT_STEP; i++ )
			{
				s_desired_append_char( s, L' ', 0, indent, prompt_width );
			}
			break;
		}
            
		case L'\r':
		{
            line_t &current = s->desired.line(line_no);
            current.clear();
            s->desired.cursor.x = 0;
			break;
		}
            
		default:
		{
			int screen_width = common_get_width();
			int cw = fish_wcwidth(b);
			
            s->desired.create_line(line_no);
            
			/*
             Check if we are at the end of the line. If so, continue on the next line.
             */
			if( (s->desired.cursor.x + cw) > screen_width )
			{
                line_no = (int)s->desired.line_count();
                s->desired.add_line();
				s->desired.cursor.y++;
				s->desired.cursor.x=0;
                for( size_t i=0; i < prompt_width; i++ )
                {
                    s_desired_append_char( s, L' ', 0, indent, prompt_width );
                }
			}
			
            line_t &line = s->desired.line(line_no);
            line.append(b, c);
			s->desired.cursor.x+= cw;
			break;
		}
	}
	
}
Ejemplo n.º 11
0
void write_screen( const wcstring &msg, wcstring &buff )
{
	const wchar_t *start, *pos;
	int line_width = 0;
	int tok_width = 0;
	int screen_width = common_get_width();
	
	if( screen_width )
	{
		start = pos = msg.c_str();
		while( 1 )
		{
			int overflow = 0;
		
			tok_width=0;

			/*
			  Tokenize on whitespace, and also calculate the width of the token
			*/
			while( *pos && ( !wcschr( L" \n\r\t", *pos ) ) )
			{
				
				/*
				  Check is token is wider than one line.
				  If so we mark it as an overflow and break the token.
				*/
				if((tok_width + wcwidth(*pos)) > (screen_width-1))
				{
					overflow = 1;
					break;				
				}
			
				tok_width += wcwidth( *pos );
				pos++;
			}

			/*
			  If token is zero character long, we don't do anything
			*/
			if( pos == start )
			{
				start = pos = pos+1;
			}
			else if( overflow )
			{
				/*
				  In case of overflow, we print a newline, except if we already are at position 0
				*/
				wchar_t *token = wcsndup( start, pos-start );
				if( line_width != 0 )
                    buff.push_back(L'\n');
                buff.append(format_string(L"%ls-\n", token));
				free( token );
				line_width=0;
			}
			else
			{
				/*
				  Print the token
				*/
				wchar_t *token = wcsndup( start, pos-start );
				if( (line_width + (line_width!=0?1:0) + tok_width) > screen_width )
				{
                    buff.push_back(L'\n');
					line_width=0;
				}
                buff.append(format_string(L"%ls%ls", line_width?L" ":L"", token ));
				free( token );
				line_width += (line_width!=0?1:0) + tok_width;
			}
			
			/*
			  Break on end of string
			*/
			if( !*pos )
			{
				break;
			}
			
			start=pos;
		}
	}
	else
	{
        buff.append(msg);
	}
    buff.push_back(L'\n');
}
Ejemplo n.º 12
0
/**
   Update the screen to match the desired output.
*/
static void s_update(screen_t *scr, const wchar_t *left_prompt, const wchar_t *right_prompt)
{
    //if (test_stuff(scr)) return;
    const size_t left_prompt_width = calc_prompt_layout(left_prompt).last_line_width;
    const size_t right_prompt_width = calc_prompt_layout(right_prompt).last_line_width;

    int screen_width = common_get_width();

    /* Figure out how many following lines we need to clear (probably 0) */
    size_t actual_lines_before_reset = scr->actual_lines_before_reset;
    scr->actual_lines_before_reset = 0;

    data_buffer_t output;

    bool need_clear_lines = scr->need_clear_lines;
    bool need_clear_screen = scr->need_clear_screen;
    bool has_cleared_screen = false;

    if (scr->actual_width != screen_width)
    {
        /* Ensure we don't issue a clear screen for the very first output, to avoid https://github.com/fish-shell/fish-shell/issues/402 */
        if (scr->actual_width != SCREEN_WIDTH_UNINITIALIZED)
        {
            need_clear_screen = true;
            s_move(scr, &output, 0, 0);
            s_reset(scr, screen_reset_current_line_contents);

            need_clear_lines = need_clear_lines || scr->need_clear_lines;
            need_clear_screen = need_clear_screen || scr->need_clear_screen;
        }
        scr->actual_width = screen_width;
    }

    scr->need_clear_lines = false;
    scr->need_clear_screen = false;

    /* 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_lines = true;
    }

    if (wcscmp(left_prompt, scr->actual_left_prompt.c_str()))
    {
        s_move(scr, &output, 0, 0);
        s_write_str(&output, left_prompt);
        scr->actual_left_prompt = left_prompt;
        scr->actual.cursor.x = (int)left_prompt_width;
    }

    for (size_t i=0; i < scr->desired.line_count(); i++)
    {
        const line_t &o_line = scr->desired.line(i);
        line_t &s_line = scr->actual.create_line(i);
        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_screen && 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;

        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 we're soft wrapped, and if we're going to change the first character of the next line, don't skip over the last two characters so that we maintain soft-wrapping */
            if (o_line.is_soft_wrapped && i + 1 < scr->desired.line_count())
            {
                bool first_character_of_next_line_will_change = true;
                if (i + 1 < scr->actual.line_count())
                {
                    if (line_shared_prefix(scr->desired.line(i+1), scr->actual.line(i+1)) > 0)
                    {
                        first_character_of_next_line_will_change = false;
                    }
                }
                if (first_character_of_next_line_will_change)
                {
                    skip_remaining = mini(skip_remaining, (size_t)(scr->actual_width - 2));
                }
            }
        }

        /* Skip over skip_remaining width worth of characters */
        size_t j = 0;
        for (; j < o_line.size(); j++)
        {
            int width = fish_wcwidth_min_0(o_line.char_at(j));
            if (skip_remaining < width)
                break;
            skip_remaining -= width;
            current_width += width;
        }

        /* Skip over zero-width characters (e.g. combining marks at the end of the prompt) */
        for (; j < o_line.size(); j++)
        {
            int width = fish_wcwidth_min_0(o_line.char_at(j));
            if (width > 0)
                break;
        }

        /* Now actually output stuff */
        for (; j < o_line.size(); j++)
        {
            /* If we are about to output into the last column, clear the screen first. If we clear the screen after we output into the last column, it can erase the last character due to the sticky right cursor. If we clear the screen too early, we can defeat soft wrapping. */
            if (j + 1 == screen_width && should_clear_screen_this_line && ! has_cleared_screen)
            {
                s_move(scr, &output, current_width, (int)i);
                s_write_mbs(&output, clr_eos);
                has_cleared_screen = true;
            }

            perform_any_impending_soft_wrap(scr, current_width, (int)i);
            s_move(scr, &output, current_width, (int)i);
            s_set_color(scr, &output, o_line.color_at(j));
            s_write_char(scr, &output, o_line.char_at(j));
            current_width += fish_wcwidth_min_0(o_line.char_at(j));
        }

        /* Clear the screen if we have not done so yet. */
        if (should_clear_screen_this_line && ! has_cleared_screen)
        {
            s_move(scr, &output, current_width, (int)i);
            s_write_mbs(&output, clr_eos);
            has_cleared_screen = true;
        }

        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 (has_cleared_screen)
        {
            /* Already cleared everything */
            clear_remainder = false;
        }
        else if (need_clear_lines && current_width < screen_width)
        {
            clear_remainder = true;
        }
        else if (right_prompt_width < scr->last_right_prompt_width)
        {
            clear_remainder = true;
        }
        else
        {
            int prev_width = (s_line.text.empty() ? 0 : fish_wcswidth(&s_line.text.at(0), s_line.text.size()));
            clear_remainder = prev_width > current_width;

        }
        if (clear_remainder)
        {
            s_set_color(scr, &output, 0xffffffff);
            s_move(scr, &output, current_width, (int)i);
            s_write_mbs(&output, clr_eol);
        }

        /* 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), (int)i);
            s_set_color(scr, &output, 0xffffffff);
            s_write_str(&output, right_prompt);
            scr->actual.cursor.x += right_prompt_width;

            /* We output in the last column. Some terms (Linux) push the cursor further right, past the window. Others make it "stick." Since we don't really know which is which, issue a cr so it goes back to the left.

               However, if the user is resizing the window smaller, then it's possible the cursor wrapped. If so, then a cr will go to the beginning of the following line! So instead issue a bunch of "move left" commands to get back onto the line, and then jump to the front of it (!)
            */

            s_move(scr, &output, scr->actual.cursor.x - (int)right_prompt_width, scr->actual.cursor.y);
            s_write_str(&output, L"\r");
            scr->actual.cursor.x = 0;
        }
    }


    /* Clear remaining lines (if any) if we haven't cleared the screen. */
    if (! has_cleared_screen && scr->desired.line_count() < lines_with_stuff)
    {
        s_set_color(scr, &output, 0xffffffff);
        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);

    if (! output.empty())
    {
        write_loop(1, &output.at(0), output.size());
    }

    /* We have now synced our actual screen against our desired screen. Note that this is a big assignment! */
    scr->actual = scr->desired;
    scr->last_right_prompt_width = right_prompt_width;
}
Ejemplo n.º 13
0
/**
   Write the bytes needed to move screen cursor to the specified
   position to the specified buffer. The actual_cursor field of the
   specified screen_t will be updated.

   \param s the screen to operate on
   \param b the buffer to send the output escape codes to
   \param new_x the new x position
   \param new_y the new y position
*/
static void s_move(screen_t *s, data_buffer_t *b, int new_x, int new_y)
{
    if (s->actual.cursor.x == new_x && s->actual.cursor.y == new_y)
        return;

    // If we are at the end of our window, then either the cursor stuck to the edge or it didn't. We don't know! We can fix it up though.
    if (s->actual.cursor.x == common_get_width())
    {
        // Either issue a cr to go back to the beginning of this line, or a nl to go to the beginning of the next one, depending on what we think is more efficient
        if (new_y <= s->actual.cursor.y)
        {
            b->push_back('\r');
        }
        else
        {
            b->push_back('\n');
            s->actual.cursor.y++;
        }
        // Either way we're not in the first column
        s->actual.cursor.x = 0;
    }

    int i;
    int x_steps, y_steps;

    char *str;
    /*
      debug( 0, L"move from %d %d to %d %d",
      s->screen_cursor[0], s->screen_cursor[1],
      new_x, new_y );
    */
    scoped_buffer_t scoped_buffer(b);

    y_steps = new_y - s->actual.cursor.y;

    if (y_steps > 0 && (strcmp(cursor_down, "\n")==0))
    {
        /*
          This is very strange - it seems some (all?) 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
          as moving it down one step. The cursor_up does not have this
          behaviour...
        */
        s->actual.cursor.x=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->actual.cursor.x;

    if (x_steps && new_x == 0)
    {
        b->push_back('\r');
        x_steps = 0;
    }

    if (x_steps < 0)
    {
        str = cursor_left;
    }
    else
    {
        str = cursor_right;
    }

    for (i=0; i<abs(x_steps); i++)
    {
        writembs(str);
    }


    s->actual.cursor.x = new_x;
    s->actual.cursor.y = new_y;
}
Ejemplo n.º 14
0
/**
   Appends a character to the end of the line that the output cursor is
   on. This function automatically handles linebreaks and lines longer
   than the screen width.
*/
static void s_desired_append_char(screen_t *s,
                                  wchar_t b,
                                  int c,
                                  int indent,
                                  size_t prompt_width)
{
    int line_no = s->desired.cursor.y;

    switch (b)
    {
        case L'\n':
        {
            int i;
            /* Current line is definitely hard wrapped */
            s->desired.create_line(s->desired.line_count());
            s->desired.line(s->desired.cursor.y).is_soft_wrapped = false;
            s->desired.cursor.y++;
            s->desired.cursor.x=0;
            for (i=0; i < prompt_width+indent*INDENT_STEP; i++)
            {
                s_desired_append_char(s, L' ', 0, indent, prompt_width);
            }
            break;
        }

        case L'\r':
        {
            line_t &current = s->desired.line(line_no);
            current.clear();
            s->desired.cursor.x = 0;
            break;
        }

        default:
        {
            int screen_width = common_get_width();
            int cw = fish_wcwidth_min_0(b);

            s->desired.create_line(line_no);

            /*
                   Check if we are at the end of the line. If so, continue on the next line.
                   */
            if ((s->desired.cursor.x + cw) > screen_width)
            {
                /* Current line is soft wrapped (assuming we support it) */
                s->desired.line(s->desired.cursor.y).is_soft_wrapped = true;
                //fprintf(stderr, "\n\n1 Soft wrapping %d\n\n", s->desired.cursor.y);

                line_no = (int)s->desired.line_count();
                s->desired.add_line();
                s->desired.cursor.y++;
                s->desired.cursor.x=0;
                for (size_t i=0; i < prompt_width; i++)
                {
                    s_desired_append_char(s, L' ', 0, indent, prompt_width);
                }
            }

            line_t &line = s->desired.line(line_no);
            line.append(b, c);
            s->desired.cursor.x+= cw;

            /* Maybe wrap the cursor to the next line, even if the line itself did not wrap. This avoids wonkiness in the last column. */
            if (s->desired.cursor.x >= screen_width)
            {
                line.is_soft_wrapped = true;
                s->desired.cursor.x = 0;
                s->desired.cursor.y++;
            }
            break;
        }
    }

}
Ejemplo n.º 15
0
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(STDOUT_FILENO, "\r", 1);
        write_loop(STDOUT_FILENO, prompt_narrow.c_str(), prompt_narrow.size());
        write_loop(STDOUT_FILENO, 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);

    /* Determine whether, if we have an autosuggestion, it was truncated */
    s->autosuggestion_is_truncated = ! autosuggestion.empty() && autosuggestion != layout.autosuggestion;

    /* 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);
}