From c43f2dce1b0ca64b43f7614d4da52bc9f2c195fe Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 22 Dec 2020 09:22:14 +0000 Subject: Break cursor movement in grid into a common set of functions that can handle line wrapping and so on in one place and use them for the obvious copy mode commands. From Anindya Mukherjee. --- grid-reader.c | 303 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 303 insertions(+) create mode 100644 grid-reader.c (limited to 'grid-reader.c') diff --git a/grid-reader.c b/grid-reader.c new file mode 100644 index 00000000..a1af3aaa --- /dev/null +++ b/grid-reader.c @@ -0,0 +1,303 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2020 Anindya Mukherjee + * + * 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" + +/* 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) +{ + u_int px; + struct grid_cell gc; + + if (all) + px = gr->gd->sx; + else + px = grid_reader_line_length(gr); + + 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) +{ + 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) { + 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); +} + +/* Check if character under cursor is in set. */ +int +grid_reader_in_set(struct grid_reader *gr, const char *set) +{ + struct grid_cell gc; + + grid_get_cell(gr->gd, gr->cx, gr->cy, &gc); + if (gc.flags & GRID_FLAG_PADDING) + return (0); + return (utf8_cstrhas(set, &gc.data)); +} + +/* 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; + int expected = 0; + + /* Do not break up wrapped words. */ + if (grid_get_line(gr->gd, gr->cy)->flags & GRID_LINE_WRAPPED) + xx = grid_reader_line_length(gr) - 1; + else + xx = grid_reader_line_length(gr); + yy = gr->gd->hsize + gr->gd->sy - 1; + + /* + * If we started inside a word, skip over word characters. Then skip + * over separators till the next word. + * + * expected is initially set to 0 for the former and then 1 for the + * latter. It is finally set to 0 when the beginning of the next word is + * found. + */ + do { + while (gr->cx > xx || + grid_reader_in_set(gr, separators) == expected) { + /* Move down if we are past the end of the line. */ + if (gr->cx > xx) { + if (gr->cy == yy) + return; + 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 = grid_reader_line_length(gr) - 1; + else + xx = grid_reader_line_length(gr); + } else + gr->cx++; + } + expected = !expected; + } while (expected == 1); +} + +/* 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; + int expected = 1; + + /* Do not break up wrapped words. */ + if (grid_get_line(gr->gd, gr->cy)->flags & GRID_LINE_WRAPPED) + xx = grid_reader_line_length(gr) - 1; + else + xx = grid_reader_line_length(gr); + yy = gr->gd->hsize + gr->gd->sy - 1; + + /* + * If we started on a separator, skip over separators. Then skip over + * word characters till the next separator. + * + * expected is initially set to 1 for the former and then 1 for the + * latter. It is finally set to 1 when the end of the next word is + * found. + */ + do { + while (gr->cx > xx || + grid_reader_in_set(gr, separators) == expected) { + /* Move down if we are past the end of the line. */ + if (gr->cx > xx) { + if (gr->cy == yy) + return; + 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 = grid_reader_line_length(gr) - 1; + else + xx = grid_reader_line_length(gr); + } else + gr->cx++; + } + expected = !expected; + } while (expected == 0); +} + +/* 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 oldx, oldy, r; + + /* Move back to the previous word character. */ + if (already || grid_reader_in_set(gr, separators)) { + for (;;) { + if (gr->cx > 0) { + gr->cx--; + if (!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 (gr->cx > 0) { + oldx = gr->cx; + gr->cx--; + r = grid_reader_in_set(gr, separators); + gr->cx = oldx; + if (r) + break; + } + } + } + } + + /* 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, 0); + } + if (gr->cx > 0) + gr->cx--; + } while (!grid_reader_in_set(gr, separators)); + gr->cx = oldx; + gr->cy = oldy; +} -- cgit v1.2.3