osc: add support for OSC 133;A (prompt markers)

This patch adds support for the OSC-133;A sequence, introduced by
FinalTerm and implemented by iTerm2, Kitty and more. See
https://iterm2.com/documentation-one-page.html#documentation-escape-codes.html.

The shell emits the OSC just before printing the prompt. This lets the
terminal know where, in the scrollback, there are prompts.

We implement this using a simple boolean in the row struct ("this row
has a prompt"). The prompt marker must be reflowed along with the text
on window resizes.

In an ideal world, erasing, or overwriting the cell where the OSC was
emitted, would remove the prompt mark. Since we don't store this
information in the cell struct, we can't do that. The best we can do
is reset it in erase_line(). This works well enough in the "normal"
screen, when used with a "normal" shell. It doesn't really work in
fullscreen apps, on the alt screen. But that doesn't matter since we
don't support jumping between prompts on the alt screen anyway.

To be able to jump between prompts, two new key bindings have been
added: prompt-prev and prompt-next, bound to ctrl+shift+z and
ctrl+shift+x respectively.

prompt-prev will jump to the previous, not currently visible, prompt,
by moving the viewport, ensuring the prompt is at the top of the
screen.

prompt-next jumps to the next prompt, visible or not. Again, by moving
the viewport to ensure the prompt is at the top of the screen. If
we're at the bottom of the scrollback, the viewport is instead moved
as far down as possible.

Closes #30
This commit is contained in:
Daniel Eklöf 2022-06-15 18:44:23 +02:00
parent 96f23b4c64
commit bdb79e8b9f
No known key found for this signature in database
GPG key ID: 5BBD4992C116573F
15 changed files with 285 additions and 18 deletions

83
input.c
View file

@ -23,8 +23,9 @@
#define LOG_MODULE "input"
#define LOG_ENABLE_DBG 0
#include "log.h"
#include "config.h"
#include "commands.h"
#include "config.h"
#include "grid.h"
#include "keymap.h"
#include "kitty-keymap.h"
#include "macros.h"
@ -335,6 +336,86 @@ execute_binding(struct seat *seat, struct terminal *term,
term_to_slave(term, binding->aux->text.data, binding->aux->text.len);
return true;
case BIND_ACTION_PROMPT_PREV: {
if (term->grid != &term->normal)
return false;
struct grid *grid = term->grid;
const int sb_start =
grid_sb_start_ignore_uninitialized(grid, term->rows);
/* Check each row from current view-1 (that is, the first
* currently not visible row), up to, and including, the
* scrollback start */
for (int r_sb_rel =
grid_row_abs_to_sb_precalc_sb_start(
grid, sb_start, grid->view) - 1;
r_sb_rel >= 0; r_sb_rel--)
{
const int r_abs =
grid_row_sb_to_abs_precalc_sb_start(grid, sb_start, r_sb_rel);
const struct row *row = grid->rows[r_abs];
xassert(row != NULL);
if (!row->prompt_marker)
continue;
grid->view = r_abs;
term_damage_view(term);
render_refresh(term);
break;
}
return true;
}
case BIND_ACTION_PROMPT_NEXT: {
if (term->grid != &term->normal)
return false;
struct grid *grid = term->grid;
const int num_rows = grid->num_rows;
if (grid->view == grid->offset) {
/* Already at the bottom */
return true;
}
/* Check each row from view+1, to the bottom of the scrollback */
for (int r_abs = (grid->view + 1) & (num_rows - 1);
;
r_abs = (r_abs + 1) & (num_rows - 1))
{
const struct row *row = grid->rows[r_abs];
xassert(row != NULL);
if (!row->prompt_marker) {
if (r_abs == grid->offset + term->rows - 1) {
/* Weve reached the bottom of the scrollback */
break;
}
continue;
}
int sb_start = grid_sb_start_ignore_uninitialized(grid, term->rows);
int ofs_sb_rel =
grid_row_abs_to_sb_precalc_sb_start(grid, sb_start, grid->offset);
int new_view_sb_rel =
grid_row_abs_to_sb_precalc_sb_start(grid, sb_start, r_abs);
new_view_sb_rel = min(ofs_sb_rel, new_view_sb_rel);
grid->view = grid_row_sb_to_abs_precalc_sb_start(
grid, sb_start, new_view_sb_rel);
term_damage_view(term);
render_refresh(term);
break;
}
return true;
}
case BIND_ACTION_SELECT_BEGIN:
selection_start(
term, seat->mouse.col, seat->mouse.row, SELECTION_CHAR_WISE, false);