summaryrefslogtreecommitdiffstats
path: root/grid-reader.c
diff options
context:
space:
mode:
Diffstat (limited to 'grid-reader.c')
-rw-r--r--grid-reader.c429
1 files changed, 429 insertions, 0 deletions
diff --git a/grid-reader.c b/grid-reader.c
new file mode 100644
index 0000000..c14e3d3
--- /dev/null
+++ b/grid-reader.c
@@ -0,0 +1,429 @@
+/* $OpenBSD$ */
+
+/*
+ * 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)
+{
+ 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, 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)
+{
+ 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;
+
+ /* 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) &&
+ grid_reader_in_set(gr, WHITESPACE))
+ gr->cx++;
+}
+
+/* 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;
+}
+
+/* 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 (!(gc.flags & GRID_FLAG_PADDING) &&
+ gc.data.size == jc->size &&
+ memcmp(gc.data.data, jc->data, gc.data.size) == 0) {
+ 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 (!(gc.flags & GRID_FLAG_PADDING) &&
+ gc.data.size == jc->size &&
+ memcmp(gc.data.data, jc->data, gc.data.size) == 0) {
+ 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 != ' ') {
+ 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;
+}