mirror of
https://github.com/tmux/tmux.git
synced 2026-07-22 16:52:42 +00:00
additional payload (to reduce problems with lifetime of objects) and are delivered to one or more event sinks. This is more powerful and reduces the complex dependencies between control mode and hooks. Events are now used for hooks, control mode notifications and for monitors (set-hook -B). wait-for can now wait for an event to fire (-E flag, with -F to for filter), with -v to print the payload, as well as listing (-l) waiting clients on an event and forcing one to wake (-w). A few additional hooks are also now available (pane-created, pane-resized, etc) and some of the existing ones have additional format variables available.
442 lines
11 KiB
C
442 lines
11 KiB
C
/* $OpenBSD: grid-reader.c,v 1.10 2026/05/17 13:12:21 nicm Exp $ */
|
|
|
|
/*
|
|
* Copyright (c) 2020 Anindya Mukherjee <anindya49@hotmail.com>
|
|
*
|
|
* Permission to use, copy, modify, and distribute this software for any
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
* copyright notice and this permission notice appear in all copies.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
|
|
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
|
|
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
|
|
#include "tmux.h"
|
|
#include <string.h>
|
|
|
|
/* Initialise virtual cursor. */
|
|
void
|
|
grid_reader_start(struct grid_reader *gr, struct grid *gd, u_int cx, u_int cy)
|
|
{
|
|
gr->gd = gd;
|
|
gr->cx = cx;
|
|
gr->cy = cy;
|
|
}
|
|
|
|
/* Get cursor position from reader. */
|
|
void
|
|
grid_reader_get_cursor(struct grid_reader *gr, u_int *cx, u_int *cy)
|
|
{
|
|
*cx = gr->cx;
|
|
*cy = gr->cy;
|
|
}
|
|
|
|
/* Get length of line containing the cursor. */
|
|
u_int
|
|
grid_reader_line_length(struct grid_reader *gr)
|
|
{
|
|
return (grid_line_length(gr->gd, gr->cy));
|
|
}
|
|
|
|
/* Move cursor forward one position. */
|
|
void
|
|
grid_reader_cursor_right(struct grid_reader *gr, int wrap, int all, int onemore)
|
|
{
|
|
u_int px;
|
|
struct grid_cell gc;
|
|
|
|
if (all)
|
|
px = gr->gd->sx;
|
|
else if (onemore)
|
|
px = grid_reader_line_length(gr);
|
|
else {
|
|
px = grid_reader_line_length(gr);
|
|
if (px != 0)
|
|
px--;
|
|
}
|
|
|
|
if (wrap && gr->cx >= px && gr->cy < gr->gd->hsize + gr->gd->sy - 1) {
|
|
grid_reader_cursor_start_of_line(gr, 0);
|
|
grid_reader_cursor_down(gr);
|
|
} else if (gr->cx < px) {
|
|
gr->cx++;
|
|
while (gr->cx < px) {
|
|
grid_get_cell(gr->gd, gr->cx, gr->cy, &gc);
|
|
if (~gc.flags & GRID_FLAG_PADDING)
|
|
break;
|
|
gr->cx++;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Move cursor back one position. */
|
|
void
|
|
grid_reader_cursor_left(struct grid_reader *gr, int wrap)
|
|
{
|
|
struct grid_cell gc;
|
|
|
|
while (gr->cx > 0) {
|
|
grid_get_cell(gr->gd, gr->cx, gr->cy, &gc);
|
|
if (~gc.flags & GRID_FLAG_PADDING)
|
|
break;
|
|
gr->cx--;
|
|
}
|
|
if (gr->cx == 0 && gr->cy > 0 &&
|
|
(wrap ||
|
|
grid_get_line(gr->gd, gr->cy - 1)->flags & GRID_LINE_WRAPPED)) {
|
|
grid_reader_cursor_up(gr);
|
|
grid_reader_cursor_end_of_line(gr, 0, 0);
|
|
} else if (gr->cx > 0)
|
|
gr->cx--;
|
|
}
|
|
|
|
/* Move cursor down one line. */
|
|
void
|
|
grid_reader_cursor_down(struct grid_reader *gr)
|
|
{
|
|
struct grid_cell gc;
|
|
|
|
if (gr->cy < gr->gd->hsize + gr->gd->sy - 1)
|
|
gr->cy++;
|
|
while (gr->cx > 0) {
|
|
grid_get_cell(gr->gd, gr->cx, gr->cy, &gc);
|
|
if (~gc.flags & GRID_FLAG_PADDING)
|
|
break;
|
|
gr->cx--;
|
|
}
|
|
}
|
|
|
|
/* Move cursor up one line. */
|
|
void
|
|
grid_reader_cursor_up(struct grid_reader *gr)
|
|
{
|
|
struct grid_cell gc;
|
|
|
|
if (gr->cy > 0)
|
|
gr->cy--;
|
|
while (gr->cx > 0) {
|
|
grid_get_cell(gr->gd, gr->cx, gr->cy, &gc);
|
|
if (~gc.flags & GRID_FLAG_PADDING)
|
|
break;
|
|
gr->cx--;
|
|
}
|
|
}
|
|
|
|
/* Move cursor to the start of the line. */
|
|
void
|
|
grid_reader_cursor_start_of_line(struct grid_reader *gr, int wrap)
|
|
{
|
|
if (wrap) {
|
|
while (gr->cy > 0 &&
|
|
grid_get_line(gr->gd, gr->cy - 1)->flags &
|
|
GRID_LINE_WRAPPED)
|
|
gr->cy--;
|
|
}
|
|
gr->cx = 0;
|
|
}
|
|
|
|
/* Move cursor to the end of the line. */
|
|
void
|
|
grid_reader_cursor_end_of_line(struct grid_reader *gr, int wrap, int all)
|
|
{
|
|
u_int yy;
|
|
|
|
if (wrap) {
|
|
yy = gr->gd->hsize + gr->gd->sy - 1;
|
|
while (gr->cy < yy && grid_get_line(gr->gd, gr->cy)->flags &
|
|
GRID_LINE_WRAPPED)
|
|
gr->cy++;
|
|
}
|
|
if (all)
|
|
gr->cx = gr->gd->sx;
|
|
else
|
|
gr->cx = grid_reader_line_length(gr);
|
|
}
|
|
|
|
/* Handle line wrapping while moving the cursor. */
|
|
static int
|
|
grid_reader_handle_wrap(struct grid_reader *gr, u_int *xx, u_int *yy)
|
|
{
|
|
/*
|
|
* Make sure the cursor lies within the grid reader's bounding area,
|
|
* wrapping to the next line as necessary. Return zero if the cursor
|
|
* would wrap past the bottom of the grid.
|
|
*/
|
|
while (gr->cx > *xx) {
|
|
if (gr->cy == *yy)
|
|
return (0);
|
|
grid_reader_cursor_start_of_line(gr, 0);
|
|
grid_reader_cursor_down(gr);
|
|
|
|
if (grid_get_line(gr->gd, gr->cy)->flags & GRID_LINE_WRAPPED)
|
|
*xx = gr->gd->sx - 1;
|
|
else
|
|
*xx = grid_reader_line_length(gr);
|
|
}
|
|
return (1);
|
|
}
|
|
|
|
/* Check if character under cursor is in set. */
|
|
int
|
|
grid_reader_in_set(struct grid_reader *gr, const char *set)
|
|
{
|
|
return (grid_in_set(gr->gd, gr->cx, gr->cy, set));
|
|
}
|
|
|
|
/* Move cursor to the start of the next word. */
|
|
void
|
|
grid_reader_cursor_next_word(struct grid_reader *gr, const char *separators)
|
|
{
|
|
u_int xx, yy, width;
|
|
|
|
/* Do not break up wrapped words. */
|
|
if (grid_get_line(gr->gd, gr->cy)->flags & GRID_LINE_WRAPPED)
|
|
xx = gr->gd->sx - 1;
|
|
else
|
|
xx = grid_reader_line_length(gr);
|
|
yy = gr->gd->hsize + gr->gd->sy - 1;
|
|
|
|
/*
|
|
* When navigating via spaces (for example with next-space) separators
|
|
* should be empty.
|
|
*
|
|
* If we started on a separator that is not whitespace, skip over
|
|
* subsequent separators that are not whitespace. Otherwise, if we
|
|
* started on a non-whitespace character, skip over subsequent
|
|
* characters that are neither whitespace nor separators. Then, skip
|
|
* over whitespace (if any) until the next non-whitespace character.
|
|
*/
|
|
if (!grid_reader_handle_wrap(gr, &xx, &yy))
|
|
return;
|
|
if (!grid_reader_in_set(gr, WHITESPACE)) {
|
|
if (grid_reader_in_set(gr, separators)) {
|
|
do
|
|
gr->cx++;
|
|
while (grid_reader_handle_wrap(gr, &xx, &yy) &&
|
|
grid_reader_in_set(gr, separators) &&
|
|
!grid_reader_in_set(gr, WHITESPACE));
|
|
} else {
|
|
do
|
|
gr->cx++;
|
|
while (grid_reader_handle_wrap(gr, &xx, &yy) &&
|
|
!(grid_reader_in_set(gr, separators) ||
|
|
grid_reader_in_set(gr, WHITESPACE)));
|
|
}
|
|
}
|
|
while (grid_reader_handle_wrap(gr, &xx, &yy) &&
|
|
(width = grid_reader_in_set(gr, WHITESPACE)))
|
|
gr->cx += width;
|
|
}
|
|
|
|
/* Move cursor to the end of the next word. */
|
|
void
|
|
grid_reader_cursor_next_word_end(struct grid_reader *gr, const char *separators)
|
|
{
|
|
u_int xx, yy;
|
|
|
|
/* Do not break up wrapped words. */
|
|
if (grid_get_line(gr->gd, gr->cy)->flags & GRID_LINE_WRAPPED)
|
|
xx = gr->gd->sx - 1;
|
|
else
|
|
xx = grid_reader_line_length(gr);
|
|
yy = gr->gd->hsize + gr->gd->sy - 1;
|
|
|
|
/*
|
|
* When navigating via spaces (for example with next-space), separators
|
|
* should be empty in both modes.
|
|
*
|
|
* If we started on a whitespace, move until reaching the first
|
|
* non-whitespace character. If that character is a separator, treat
|
|
* subsequent separators as a word, and continue moving until the first
|
|
* non-separator. Otherwise, continue moving until the first separator
|
|
* or whitespace.
|
|
*/
|
|
|
|
while (grid_reader_handle_wrap(gr, &xx, &yy)) {
|
|
if (grid_reader_in_set(gr, WHITESPACE))
|
|
gr->cx++;
|
|
else if (grid_reader_in_set(gr, separators)) {
|
|
do
|
|
gr->cx++;
|
|
while (grid_reader_handle_wrap(gr, &xx, &yy) &&
|
|
grid_reader_in_set(gr, separators) &&
|
|
!grid_reader_in_set(gr, WHITESPACE));
|
|
return;
|
|
} else {
|
|
do
|
|
gr->cx++;
|
|
while (grid_reader_handle_wrap(gr, &xx, &yy) &&
|
|
!(grid_reader_in_set(gr, WHITESPACE) ||
|
|
grid_reader_in_set(gr, separators)));
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Move to the previous place where a word begins. */
|
|
void
|
|
grid_reader_cursor_previous_word(struct grid_reader *gr, const char *separators,
|
|
int already, int stop_at_eol)
|
|
{
|
|
int oldx, oldy, at_eol, word_is_letters;
|
|
|
|
/* Move back to the previous word character. */
|
|
if (already || grid_reader_in_set(gr, WHITESPACE)) {
|
|
for (;;) {
|
|
if (gr->cx > 0) {
|
|
gr->cx--;
|
|
if (!grid_reader_in_set(gr, WHITESPACE)) {
|
|
word_is_letters =
|
|
!grid_reader_in_set(gr, separators);
|
|
break;
|
|
}
|
|
} else {
|
|
if (gr->cy == 0)
|
|
return;
|
|
grid_reader_cursor_up(gr);
|
|
grid_reader_cursor_end_of_line(gr, 0, 0);
|
|
|
|
/* Stop if separator at EOL. */
|
|
if (stop_at_eol && gr->cx > 0) {
|
|
oldx = gr->cx;
|
|
gr->cx--;
|
|
at_eol = grid_reader_in_set(gr,
|
|
WHITESPACE);
|
|
gr->cx = oldx;
|
|
if (at_eol) {
|
|
word_is_letters = 0;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else
|
|
word_is_letters = !grid_reader_in_set(gr, separators);
|
|
|
|
/* Move back to the beginning of this word. */
|
|
do {
|
|
oldx = gr->cx;
|
|
oldy = gr->cy;
|
|
if (gr->cx == 0) {
|
|
if (gr->cy == 0 ||
|
|
(~grid_get_line(gr->gd, gr->cy - 1)->flags &
|
|
GRID_LINE_WRAPPED))
|
|
break;
|
|
grid_reader_cursor_up(gr);
|
|
grid_reader_cursor_end_of_line(gr, 0, 1);
|
|
}
|
|
if (gr->cx > 0)
|
|
gr->cx--;
|
|
} while (!grid_reader_in_set(gr, WHITESPACE) &&
|
|
word_is_letters != grid_reader_in_set(gr, separators));
|
|
gr->cx = oldx;
|
|
gr->cy = oldy;
|
|
}
|
|
|
|
/* Compare grid cell to UTF-8 data. Return 1 if equal, 0 if not. */
|
|
static int
|
|
grid_reader_cell_equals_data(const struct grid_cell *gc,
|
|
const struct utf8_data *ud)
|
|
{
|
|
if (gc->flags & GRID_FLAG_PADDING)
|
|
return (0);
|
|
if (gc->flags & GRID_FLAG_TAB && ud->size == 1 && *ud->data == '\t')
|
|
return (1);
|
|
if (gc->data.size != ud->size)
|
|
return (0);
|
|
return (memcmp(gc->data.data, ud->data, gc->data.size) == 0);
|
|
}
|
|
|
|
/* Jump forward to character. */
|
|
int
|
|
grid_reader_cursor_jump(struct grid_reader *gr, const struct utf8_data *jc)
|
|
{
|
|
struct grid_cell gc;
|
|
u_int px, py, xx, yy;
|
|
|
|
px = gr->cx;
|
|
yy = gr->gd->hsize + gr->gd->sy - 1;
|
|
|
|
for (py = gr->cy; py <= yy; py++) {
|
|
xx = grid_line_length(gr->gd, py);
|
|
while (px < xx) {
|
|
grid_get_cell(gr->gd, px, py, &gc);
|
|
if (grid_reader_cell_equals_data(&gc, jc)) {
|
|
gr->cx = px;
|
|
gr->cy = py;
|
|
return (1);
|
|
}
|
|
px++;
|
|
}
|
|
|
|
if (py == yy ||
|
|
!(grid_get_line(gr->gd, py)->flags & GRID_LINE_WRAPPED))
|
|
return (0);
|
|
px = 0;
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
/* Jump back to character. */
|
|
int
|
|
grid_reader_cursor_jump_back(struct grid_reader *gr, const struct utf8_data *jc)
|
|
{
|
|
struct grid_cell gc;
|
|
u_int px, py, xx;
|
|
|
|
xx = gr->cx + 1;
|
|
|
|
for (py = gr->cy + 1; py > 0; py--) {
|
|
for (px = xx; px > 0; px--) {
|
|
grid_get_cell(gr->gd, px - 1, py - 1, &gc);
|
|
if (grid_reader_cell_equals_data(&gc, jc)) {
|
|
gr->cx = px - 1;
|
|
gr->cy = py - 1;
|
|
return (1);
|
|
}
|
|
}
|
|
|
|
if (py == 1 ||
|
|
!(grid_get_line(gr->gd, py - 2)->flags & GRID_LINE_WRAPPED))
|
|
return (0);
|
|
xx = grid_line_length(gr->gd, py - 2);
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
/* Jump back to the first non-blank character of the line. */
|
|
void
|
|
grid_reader_cursor_back_to_indentation(struct grid_reader *gr)
|
|
{
|
|
struct grid_cell gc;
|
|
u_int px, py, xx, yy, oldx, oldy;
|
|
|
|
yy = gr->gd->hsize + gr->gd->sy - 1;
|
|
oldx = gr->cx;
|
|
oldy = gr->cy;
|
|
grid_reader_cursor_start_of_line(gr, 1);
|
|
|
|
for (py = gr->cy; py <= yy; py++) {
|
|
xx = grid_line_length(gr->gd, py);
|
|
for (px = 0; px < xx; px++) {
|
|
grid_get_cell(gr->gd, px, py, &gc);
|
|
if ((gc.data.size != 1 || *gc.data.data != ' ') &&
|
|
~gc.flags & GRID_FLAG_TAB &&
|
|
~gc.flags & GRID_FLAG_PADDING) {
|
|
gr->cx = px;
|
|
gr->cy = py;
|
|
return;
|
|
}
|
|
}
|
|
if (~grid_get_line(gr->gd, py)->flags & GRID_LINE_WRAPPED)
|
|
break;
|
|
}
|
|
gr->cx = oldx;
|
|
gr->cy = oldy;
|
|
}
|