aboutsummaryrefslogtreecommitdiffhomepage
path: root/screen.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'screen.cpp')
-rw-r--r--screen.cpp178
1 files changed, 119 insertions, 59 deletions
diff --git a/screen.cpp b/screen.cpp
index 5ebe8605..8eb514af 100644
--- a/screen.cpp
+++ b/screen.cpp
@@ -20,6 +20,8 @@ efficient way for transforming that to the desired screen content.
#if HAVE_NCURSES_H
#include <ncurses.h>
+#elif HAVE_NCURSES_CURSES_H
+#include <ncurses/curses.h>
#else
#include <curses.h>
#endif
@@ -45,9 +47,10 @@ efficient way for transforming that to the desired screen content.
#include "highlight.h"
#include "screen.h"
#include "env.h"
+#include "pager.h"
/** The number of characters to indent new blocks */
-#define INDENT_STEP 4
+#define INDENT_STEP 4u
/** The initial screen width */
#define SCREEN_WIDTH_UNINITIALIZED -1
@@ -137,14 +140,14 @@ static bool allow_soft_wrap(void)
size_t escape_code_length(const wchar_t *code)
{
assert(code != NULL);
-
+
/* The only escape codes we recognize start with \x1b */
if (code[0] != L'\x1b')
return 0;
-
+
size_t resulting_length = 0;
bool found = false;
-
+
if (cur_term != NULL)
{
/*
@@ -158,12 +161,12 @@ size_t escape_code_length(const wchar_t *code)
set_foreground,
set_background,
};
-
+
for (size_t p=0; p < sizeof esc / sizeof *esc && !found; p++)
{
if (!esc[p])
continue;
-
+
for (size_t k=0; k<8; k++)
{
size_t len = try_sequence(tparm(esc[p],k), code);
@@ -176,7 +179,7 @@ size_t escape_code_length(const wchar_t *code)
}
}
}
-
+
if (cur_term != NULL)
{
/*
@@ -206,9 +209,9 @@ size_t escape_code_length(const wchar_t *code)
exit_standout_mode,
enter_secure_mode
};
-
-
-
+
+
+
for (size_t p=0; p < sizeof esc2 / sizeof *esc2 && !found; p++)
{
if (!esc2[p])
@@ -226,7 +229,7 @@ size_t escape_code_length(const wchar_t *code)
}
}
}
-
+
if (!found)
{
if (code[1] == L'k')
@@ -254,6 +257,29 @@ size_t escape_code_length(const wchar_t *code)
if (! found)
{
+ /* iTerm2 escape codes: CSI followed by ], terminated by either BEL or escape + backslash. See https://code.google.com/p/iterm2/wiki/ProprietaryEscapeCodes */
+ if (code[1] == ']')
+ {
+ // Start at 2 to skip over <esc>]
+ size_t cursor = 2;
+ for (; code[cursor] != L'\0'; cursor++)
+ {
+ /* Consume a sequence of characters up to <esc>\ or <bel> */
+ if (code[cursor] == '\x07' || (code[cursor] == '\\' && code[cursor - 1] == '\x1b'))
+ {
+ found = true;
+ break;
+ }
+ }
+ if (found)
+ {
+ resulting_length = cursor + 1;
+ }
+ }
+ }
+
+ if (! found)
+ {
/* Generic VT100 one byte sequence: CSI followed by something in the range @ through _ */
if (code[1] == L'[' && (code[2] >= L'@' && code[2] <= L'_'))
{
@@ -261,7 +287,7 @@ size_t escape_code_length(const wchar_t *code)
found = true;
}
}
-
+
if (! found)
{
/* Generic VT100 CSI-style sequence. <esc>, followed by zero or more ASCII characters NOT in the range [@,_], followed by one character in that range */
@@ -273,11 +299,11 @@ size_t escape_code_length(const wchar_t *code)
{
/* Consume a sequence of ASCII characters not in the range [@, ~] */
wchar_t c = code[cursor];
-
+
/* If we're not in ASCII, just stop */
if (c > 127)
break;
-
+
/* If we're the end character, then consume it and then stop */
if (c >= L'@' && c <= L'~')
{
@@ -290,7 +316,6 @@ size_t escape_code_length(const wchar_t *code)
resulting_length = cursor;
}
}
-
if (! found)
{
/* Generic VT100 two byte sequence: <esc> followed by something in the range @ through _ */
@@ -300,7 +325,7 @@ size_t escape_code_length(const wchar_t *code)
found = true;
}
}
-
+
return resulting_length;
}
@@ -378,17 +403,6 @@ static size_t calc_prompt_lines(const wcstring &prompt)
}
return result;
}
-/**
- Test if there is space between the time fields of struct stat to
- use for sub second information. If so, we assume this space
- contains the desired information.
-*/
-static int room_for_usec(struct stat *st)
-{
- int res = ((&(st->st_atime) + 2) == &(st->st_mtime) &&
- (&(st->st_atime) + 4) == &(st->st_ctime));
- return res;
-}
/**
Stat stdout and stderr and save result.
@@ -455,11 +469,13 @@ static void s_check_status(screen_t *s)
int changed = (s->prev_buff_1.st_mtime != s->post_buff_1.st_mtime) ||
(s->prev_buff_2.st_mtime != s->post_buff_2.st_mtime);
- if (room_for_usec(&s->post_buff_1))
- {
- changed = changed || ((&s->prev_buff_1.st_mtime)[1] != (&s->post_buff_1.st_mtime)[1]) ||
- ((&s->prev_buff_2.st_mtime)[1] != (&s->post_buff_2.st_mtime)[1]);
- }
+ #if defined HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC
+ changed = changed || s->prev_buff_1.st_mtimespec.tv_nsec != s->post_buff_1.st_mtimespec.tv_nsec ||
+ s->prev_buff_2.st_mtimespec.tv_nsec != s->post_buff_2.st_mtimespec.tv_nsec;
+ #elif defined HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC
+ changed = changed || s->prev_buff_1.st_mtim.tv_nsec != s->post_buff_1.st_mtim.tv_nsec ||
+ s->prev_buff_2.st_mtim.tv_nsec != s->post_buff_2.st_mtim.tv_nsec;
+ #endif
if (changed)
{
@@ -537,10 +553,6 @@ static void s_desired_append_char(screen_t *s,
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);
@@ -650,18 +662,32 @@ static void s_move(screen_t *s, data_buffer_t *b, int new_x, int new_y)
x_steps = 0;
}
+ 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;
}
-
- for (i=0; i<abs(x_steps); i++)
+
+ // 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 (#1448)
+ bool use_multi = (multi_str != NULL && multi_str[0] != '\0' && abs(x_steps) * strlen(str) > strlen(multi_str));
+ if (use_multi)
{
- writembs(str);
+ char *multi_param = tparm(multi_str, abs(x_steps));
+ writembs(multi_param);
+ }
+ else
+ {
+ for (i=0; i<abs(x_steps); i++)
+ {
+ writembs(str);
+ }
}
@@ -672,7 +698,7 @@ static void s_move(screen_t *s, data_buffer_t *b, int new_x, int new_y)
/**
Set the pen color for the terminal
*/
-static void s_set_color(screen_t *s, data_buffer_t *b, int c)
+static void s_set_color(screen_t *s, data_buffer_t *b, highlight_spec_t c)
{
scoped_buffer_t scoped_buffer(b);
@@ -1031,7 +1057,7 @@ static void s_update(screen_t *scr, const wchar_t *left_prompt, const wchar_t *r
if (! output.empty())
{
- write_loop(1, &output.at(0), output.size());
+ write_loop(STDOUT_FILENO, &output.at(0), output.size());
}
/* We have now synced our actual screen against our desired screen. Note that this is a big assignment! */
@@ -1060,7 +1086,7 @@ struct screen_layout_t
wcstring autosuggestion;
/* Whether the prompts get their own line or not */
- bool prompts_get_own_line;
+ bool prompts_get_own_line;
};
/* Given a vector whose indexes are offsets and whose values are the widths of the string if truncated at that offset, return the offset that fits in the given width. Returns width_by_offset.size() - 1 if they all fit. The first value in width_by_offset is assumed to be 0. */
@@ -1098,7 +1124,7 @@ static screen_layout_t compute_layout(screen_t *s,
size_t left_prompt_width = left_prompt_layout.last_line_width;
size_t right_prompt_width = right_prompt_layout.last_line_width;
- if (left_prompt_layout.max_line_width >= screen_width)
+ if (left_prompt_layout.max_line_width > screen_width)
{
/* If we have a multi-line prompt, see if the longest line fits; if not neuter the whole left prompt */
left_prompt = L"> ";
@@ -1215,7 +1241,15 @@ static screen_layout_t compute_layout(screen_t *s,
result.left_prompt_space = left_prompt_width;
// See remark about for why we can't use the right prompt here
//result.right_prompt = right_prompt;
- result.prompts_get_own_line = true;
+
+ // If the command wraps, and the prompt is not short, place the command on its own line.
+ // A short prompt is 33% or less of the terminal's width.
+ const size_t prompt_percent_width = (100 * left_prompt_width) / screen_width;
+ if (left_prompt_width + first_command_line_width + 1 > screen_width && prompt_percent_width > 33)
+ {
+ result.prompts_get_own_line = true;
+ }
+
done = true;
}
@@ -1229,9 +1263,13 @@ void s_write(screen_t *s,
const wcstring &right_prompt,
const wcstring &commandline,
size_t explicit_len,
- const int *colors,
+ const highlight_spec_t *colors,
const int *indent,
- size_t cursor_pos)
+ size_t cursor_pos,
+ size_t sel_start_pos,
+ size_t sel_stop_pos,
+ const page_rendering_t &pager,
+ bool cursor_position_is_within_pager)
{
screen_data_t::cursor_t cursor_arr;
@@ -1298,26 +1336,32 @@ void s_write(screen_t *s,
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)
+ /* Grab the current cursor's x,y position if this character matches the cursor's offset */
+ if (! cursor_position_is_within_pager && i == cursor_pos)
{
cursor_arr = s->desired.cursor;
}
-
- s_desired_append_char(s, effective_commandline.at(i), color, indent[i], first_line_prompt_space);
+ s_desired_append_char(s, effective_commandline.at(i), colors[i], indent[i], first_line_prompt_space);
}
- if (i == cursor_pos)
+
+ /* Cursor may have been at the end too */
+ if (! cursor_position_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_position_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.c_str(), layout.right_prompt.c_str());
s_save_status(s);
}
@@ -1386,7 +1430,7 @@ void s_reset(screen_t *s, screen_reset_mode_t mode)
int non_space_width = wcwidth(omitted_newline_char);
if (screen_width >= non_space_width)
{
- if (output_get_supports_term256())
+ if (output_get_color_support() & color_support_term256)
{
// draw the string in term256 gray
abandon_line_string.append(L"\x1b[38;5;245m");
@@ -1423,6 +1467,22 @@ void s_reset(screen_t *s, screen_reset_mode_t mode)
fstat(2, &s->prev_buff_2);
}
+bool screen_force_clear_to_end()
+{
+ bool result = false;
+ if (clr_eos)
+ {
+ data_buffer_t output;
+ s_write_mbs(&output, clr_eos);
+ if (! output.empty())
+ {
+ write_loop(STDOUT_FILENO, &output.at(0), output.size());
+ result = true;
+ }
+ }
+ return result;
+}
+
screen_t::screen_t() :
desired(),
actual(),