1 /* $OpenBSD: grid-reader.c,v 1.4 2021/04/05 08:43:48 nicm Exp $ */ 2 3 /* 4 * Copyright (c) 2020 Anindya Mukherjee <anindya49@hotmail.com> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER 15 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING 16 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include "tmux.h" 20 #include <string.h> 21 22 /* Initialise virtual cursor. */ 23 void 24 grid_reader_start(struct grid_reader *gr, struct grid *gd, u_int cx, u_int cy) 25 { 26 gr->gd = gd; 27 gr->cx = cx; 28 gr->cy = cy; 29 } 30 31 /* Get cursor position from reader. */ 32 void 33 grid_reader_get_cursor(struct grid_reader *gr, u_int *cx, u_int *cy) 34 { 35 *cx = gr->cx; 36 *cy = gr->cy; 37 } 38 39 /* Get length of line containing the cursor. */ 40 u_int 41 grid_reader_line_length(struct grid_reader *gr) 42 { 43 return (grid_line_length(gr->gd, gr->cy)); 44 } 45 46 /* Move cursor forward one position. */ 47 void 48 grid_reader_cursor_right(struct grid_reader *gr, int wrap, int all) 49 { 50 u_int px; 51 struct grid_cell gc; 52 53 if (all) 54 px = gr->gd->sx; 55 else 56 px = grid_reader_line_length(gr); 57 58 if (wrap && gr->cx >= px && gr->cy < gr->gd->hsize + gr->gd->sy - 1) { 59 grid_reader_cursor_start_of_line(gr, 0); 60 grid_reader_cursor_down(gr); 61 } else if (gr->cx < px) { 62 gr->cx++; 63 while (gr->cx < px) { 64 grid_get_cell(gr->gd, gr->cx, gr->cy, &gc); 65 if (~gc.flags & GRID_FLAG_PADDING) 66 break; 67 gr->cx++; 68 } 69 } 70 } 71 72 /* Move cursor back one position. */ 73 void 74 grid_reader_cursor_left(struct grid_reader *gr, int wrap) 75 { 76 struct grid_cell gc; 77 78 while (gr->cx > 0) { 79 grid_get_cell(gr->gd, gr->cx, gr->cy, &gc); 80 if (~gc.flags & GRID_FLAG_PADDING) 81 break; 82 gr->cx--; 83 } 84 if (gr->cx == 0 && gr->cy > 0 && 85 (wrap || 86 grid_get_line(gr->gd, gr->cy - 1)->flags & GRID_LINE_WRAPPED)) { 87 grid_reader_cursor_up(gr); 88 grid_reader_cursor_end_of_line(gr, 0, 0); 89 } else if (gr->cx > 0) 90 gr->cx--; 91 } 92 93 /* Move cursor down one line. */ 94 void 95 grid_reader_cursor_down(struct grid_reader *gr) 96 { 97 struct grid_cell gc; 98 99 if (gr->cy < gr->gd->hsize + gr->gd->sy - 1) 100 gr->cy++; 101 while (gr->cx > 0) { 102 grid_get_cell(gr->gd, gr->cx, gr->cy, &gc); 103 if (~gc.flags & GRID_FLAG_PADDING) 104 break; 105 gr->cx--; 106 } 107 } 108 109 /* Move cursor up one line. */ 110 void 111 grid_reader_cursor_up(struct grid_reader *gr) 112 { 113 struct grid_cell gc; 114 115 if (gr->cy > 0) 116 gr->cy--; 117 while (gr->cx > 0) { 118 grid_get_cell(gr->gd, gr->cx, gr->cy, &gc); 119 if (~gc.flags & GRID_FLAG_PADDING) 120 break; 121 gr->cx--; 122 } 123 } 124 125 /* Move cursor to the start of the line. */ 126 void 127 grid_reader_cursor_start_of_line(struct grid_reader *gr, int wrap) 128 { 129 if (wrap) { 130 while (gr->cy > 0 && 131 grid_get_line(gr->gd, gr->cy - 1)->flags & 132 GRID_LINE_WRAPPED) 133 gr->cy--; 134 } 135 gr->cx = 0; 136 } 137 138 /* Move cursor to the end of the line. */ 139 void 140 grid_reader_cursor_end_of_line(struct grid_reader *gr, int wrap, int all) 141 { 142 u_int yy; 143 144 if (wrap) { 145 yy = gr->gd->hsize + gr->gd->sy - 1; 146 while (gr->cy < yy && grid_get_line(gr->gd, gr->cy)->flags & 147 GRID_LINE_WRAPPED) 148 gr->cy++; 149 } 150 if (all) 151 gr->cx = gr->gd->sx; 152 else 153 gr->cx = grid_reader_line_length(gr); 154 } 155 156 /* Check if character under cursor is in set. */ 157 int 158 grid_reader_in_set(struct grid_reader *gr, const char *set) 159 { 160 struct grid_cell gc; 161 162 grid_get_cell(gr->gd, gr->cx, gr->cy, &gc); 163 if (gc.flags & GRID_FLAG_PADDING) 164 return (0); 165 return (utf8_cstrhas(set, &gc.data)); 166 } 167 168 /* Move cursor to the start of the next word. */ 169 void 170 grid_reader_cursor_next_word(struct grid_reader *gr, const char *separators) 171 { 172 u_int xx, yy; 173 int expected = 0; 174 175 /* Do not break up wrapped words. */ 176 if (grid_get_line(gr->gd, gr->cy)->flags & GRID_LINE_WRAPPED) 177 xx = gr->gd->sx - 1; 178 else 179 xx = grid_reader_line_length(gr); 180 yy = gr->gd->hsize + gr->gd->sy - 1; 181 182 /* 183 * If we started inside a word, skip over word characters. Then skip 184 * over separators till the next word. 185 * 186 * expected is initially set to 0 for the former and then 1 for the 187 * latter. It is finally set to 0 when the beginning of the next word is 188 * found. 189 */ 190 do { 191 while (gr->cx > xx || 192 grid_reader_in_set(gr, separators) == expected) { 193 /* Move down if we are past the end of the line. */ 194 if (gr->cx > xx) { 195 if (gr->cy == yy) 196 return; 197 grid_reader_cursor_start_of_line(gr, 0); 198 grid_reader_cursor_down(gr); 199 200 if (grid_get_line(gr->gd, gr->cy)->flags & 201 GRID_LINE_WRAPPED) 202 xx = gr->gd->sx - 1; 203 else 204 xx = grid_reader_line_length(gr); 205 } else 206 gr->cx++; 207 } 208 expected = !expected; 209 } while (expected == 1); 210 } 211 212 /* Move cursor to the end of the next word. */ 213 void 214 grid_reader_cursor_next_word_end(struct grid_reader *gr, const char *separators) 215 { 216 u_int xx, yy; 217 int expected = 1; 218 219 /* Do not break up wrapped words. */ 220 if (grid_get_line(gr->gd, gr->cy)->flags & GRID_LINE_WRAPPED) 221 xx = gr->gd->sx - 1; 222 else 223 xx = grid_reader_line_length(gr); 224 yy = gr->gd->hsize + gr->gd->sy - 1; 225 226 /* 227 * If we started on a separator, skip over separators. Then skip over 228 * word characters till the next separator. 229 * 230 * expected is initially set to 1 for the former and then 1 for the 231 * latter. It is finally set to 1 when the end of the next word is 232 * found. 233 */ 234 do { 235 while (gr->cx > xx || 236 grid_reader_in_set(gr, separators) == expected) { 237 /* Move down if we are past the end of the line. */ 238 if (gr->cx > xx) { 239 if (gr->cy == yy) 240 return; 241 grid_reader_cursor_start_of_line(gr, 0); 242 grid_reader_cursor_down(gr); 243 244 if (grid_get_line(gr->gd, gr->cy)->flags & 245 GRID_LINE_WRAPPED) 246 xx = gr->gd->sx - 1; 247 else 248 xx = grid_reader_line_length(gr); 249 } else 250 gr->cx++; 251 } 252 expected = !expected; 253 } while (expected == 0); 254 } 255 256 /* Move to the previous place where a word begins. */ 257 void 258 grid_reader_cursor_previous_word(struct grid_reader *gr, const char *separators, 259 int already) 260 { 261 int oldx, oldy, r; 262 263 /* Move back to the previous word character. */ 264 if (already || grid_reader_in_set(gr, separators)) { 265 for (;;) { 266 if (gr->cx > 0) { 267 gr->cx--; 268 if (!grid_reader_in_set(gr, separators)) 269 break; 270 } else { 271 if (gr->cy == 0) 272 return; 273 grid_reader_cursor_up(gr); 274 grid_reader_cursor_end_of_line(gr, 0, 0); 275 276 /* Stop if separator at EOL. */ 277 if (gr->cx > 0) { 278 oldx = gr->cx; 279 gr->cx--; 280 r = grid_reader_in_set(gr, separators); 281 gr->cx = oldx; 282 if (r) 283 break; 284 } 285 } 286 } 287 } 288 289 /* Move back to the beginning of this word. */ 290 do { 291 oldx = gr->cx; 292 oldy = gr->cy; 293 if (gr->cx == 0) { 294 if (gr->cy == 0 || 295 ~grid_get_line(gr->gd, gr->cy - 1)->flags & 296 GRID_LINE_WRAPPED) 297 break; 298 grid_reader_cursor_up(gr); 299 grid_reader_cursor_end_of_line(gr, 0, 1); 300 } 301 if (gr->cx > 0) 302 gr->cx--; 303 } while (!grid_reader_in_set(gr, separators)); 304 gr->cx = oldx; 305 gr->cy = oldy; 306 } 307 308 /* Jump forward to character. */ 309 int 310 grid_reader_cursor_jump(struct grid_reader *gr, const struct utf8_data *jc) 311 { 312 struct grid_cell gc; 313 u_int px, py, xx, yy; 314 315 px = gr->cx; 316 yy = gr->gd->hsize + gr->gd->sy - 1; 317 318 for (py = gr->cy; py <= yy; py++) { 319 xx = grid_line_length(gr->gd, py); 320 while (px < xx) { 321 grid_get_cell(gr->gd, px, py, &gc); 322 if (!(gc.flags & GRID_FLAG_PADDING) && 323 gc.data.size == jc->size && 324 memcmp(gc.data.data, jc->data, gc.data.size) == 0) { 325 gr->cx = px; 326 gr->cy = py; 327 return 1; 328 } 329 px++; 330 } 331 332 if (py == yy || 333 !(grid_get_line(gr->gd, py)->flags & GRID_LINE_WRAPPED)) 334 return 0; 335 px = 0; 336 } 337 return 0; 338 } 339 340 /* Jump back to character. */ 341 int 342 grid_reader_cursor_jump_back(struct grid_reader *gr, const struct utf8_data *jc) 343 { 344 struct grid_cell gc; 345 u_int px, py, xx; 346 347 xx = gr->cx + 1; 348 349 for (py = gr->cy + 1; py > 0; py--) { 350 for (px = xx; px > 0; px--) { 351 grid_get_cell(gr->gd, px - 1, py - 1, &gc); 352 if (!(gc.flags & GRID_FLAG_PADDING) && 353 gc.data.size == jc->size && 354 memcmp(gc.data.data, jc->data, gc.data.size) == 0) { 355 gr->cx = px - 1; 356 gr->cy = py - 1; 357 return 1; 358 } 359 } 360 361 if (py == 1 || 362 !(grid_get_line(gr->gd, py - 2)->flags & GRID_LINE_WRAPPED)) 363 return 0; 364 xx = grid_line_length(gr->gd, py - 2); 365 } 366 return 0; 367 } 368 369 /* Jump back to the first non-blank character of the line. */ 370 void 371 grid_reader_cursor_back_to_indentation(struct grid_reader *gr) 372 { 373 struct grid_cell gc; 374 u_int px, py, xx, yy; 375 376 yy = gr->gd->hsize + gr->gd->sy - 1; 377 grid_reader_cursor_start_of_line(gr, 1); 378 379 for (py = gr->cy; py <= yy; py++) { 380 xx = grid_line_length(gr->gd, py); 381 for (px = 0; px < xx; px++) { 382 grid_get_cell(gr->gd, px, py, &gc); 383 if (gc.data.size != 1 || *gc.data.data != ' ') 384 break; 385 } 386 if (~grid_get_line(gr->gd, py)->flags & GRID_LINE_WRAPPED) 387 break; 388 } 389 } 390